Object Oriented programming in Python.
Originally part 2 of a 4 lectures seminar for the Networking class of the Computer Science course at the University of Parma
5. Espressioni regolari
(PCRE)
Modulo re
re.compile: compila l’espressione regolare
R.match, R.find, R.findall, R.finditer
M.group, M.groups
...
Utili e potenti, basta non abusare
Usare se possibile la sintassi estesa con
commenti
6. Docstring
def read_all(name_or_handle):
'''Read the whole content of specified file.
name_or_handle can be a readable file-like object
or a filename. In the latter case, the file is
opened and the contents are returned.'''
try:
return name_or_handle.read()
except AttributeError:
handle = open(name_or_handle)
try:
return handle.read()
finally:
handle.close()
7. Docstring
def read_all(name_or_handle):
'''Read the whole content of specified file.
name_or_handle can be a readable file-like object
or a filename. In the latter case, the file is
opened and the contents are returned.'''
try:
return name_or_handle.read()
except AttributeError:
handle = open(name_or_handle)
try:
return handle.read()
finally:
handle.close() Legge tutto
il contenuto,
nel modo più efficiente
8. Docstring
def read_all(name_or_handle):
Attenzione! Secontent of specified file. handle dentro al try e
'''Read the whole proviamo a legare
name_or_handle can be a readable file-like object
abbiamo un’eccezione, handle.close viene provato su un
or a filename. In the latter case, the file is
opened and the contents are returned.''' a questo livello non
handle ancora unbound. Inoltre
try: sapremmo gestire un IOError
return name_or_handle.read()
except AttributeError:
handle = open(name_or_handle)
try:
return handle.read()
finally:
handle.close() Legge tutto il contenuto,
nel modo più efficiente
9. Docstring (2)
La prima cosa nel corpo della funzione é una stringa. In
questo caso é detta “docstring”
Si applica anche a classi e moduli
Quella stringa diventa documentazione della funzione
(internamente è legata con l’attributo __doc__)
10. Docstring (2)
La prima cosa nel corpo della funzione é una stringa. In
questo caso é detta “docstring”
Si applica anche a classi e moduli
Quella stringa diventa documentazione della funzione
(internamente è legata con l’attributo __doc__)
>>> import example_docstring
>>> help(example_docstring.read_all)
11. Docstring (2)
La prima cosa nel corpo della funzione é una stringa. In
questo caso é detta “docstring”
Si applica anche a classi e moduli
Quella stringa diventa documentazione della funzione
(internamente è legata con l’attributo __doc__)
>>> import example_docstring
>>> help(example_docstring.read_all)
read_all(name_or_handle)
Read the whole content of specified file.
name_or_handle can be a readable file-like object
or a filename. In the latter case, the file is
opened and the contents are returned.
12. Named parameters
Considerate questa versione di read_all
L’utente specifica il nome di file o l’handle
di file
def read_all(filename=None, handle=None):
'''Read the whole content of specified file.'''
if handle:
return handle.read()
elif filename:
handle = open(filename)
try:
return handle.read()
finally:
handle.close()
raise TypeError, 'read_all takes one argument'
13. Named parameters
Considerate questa versione di read_all
L’utente specifica il nome di file o l’handle
di file
def read_all(filename=None, handle=None):
'''Read the whole content of specified file.'''
if handle:
return handle.read()
elif filename:
handle = open(filename)
try:
return handle.read()
finally:
handle.close()
raise TypeError, 'read_all takes one argument'
>>> improved_read_all.read_all('improved_read_all.py')
>>> improved_read_all.read_all(filename='improved_read_all.py')
>>> improved_read_all.read_all(handle=open('improved_read_all.py'))
14. Aprire un file
Il tipico modo di aprire un file é usare la
funzione open oppure il costruttore degli
oggetti file (file)
open é praticamente un alias per file
file(name[, mode[, buff]]) -> file object
Mode è una stringa che indica come il file
deve essere aperto:
lettura, scrittura, append, binario, text
buff controlla il buffering
15. Modi di apertura
Descrizione
r [Default] : Aperto in sola lettura, deve esistere
w Aperto in sola scrittura, troncato e sovrascritto
a Aperto in sola scrittura, si appende in fondo
r+ Deve esistere, aperto lettura e scrittura
w+ Troncato se esiste, aperto lett. e scritt.
a+ Aperto in lettura e scrittura, appende
t [Default] : su Win ritorna n quando trova il separatore
b Restituisce esattamente quello che trova
U Restituisce ‘n’ qualunque separatore trovi
16. Apriamo in lettura...
file_object = open('thefile.txt')
try:
for line in file_object:
# process line
# maybe:
# print line.rstrip()
pass
finally:
file_object.close()
17. Apriamo in lettura...
file_object = open('thefile.txt')
try:
for line in file_object:
# process line
# maybe:
# print line.rstrip()
pass
finally:
file_object.close()
chunk_size = 256
file_object = open('binary_file.dat', 'rb')
try:
while True:
chunk = file_object.read(256)
if not chunk:
break
# process chunk...
finally:
file_object.close()
18. Apriamo in lettura...
F.readlines() -> una lista di stringhe
chunk_size = 256
file_object = open('thefile.txt')
file_object = open('binary_file.dat', 'rb')
try:
try:
for line in file_object:
while True:
# process line
chunk = file_object.read(256)
# maybe:
if not chunk:
# print line.rstrip()
break
pass
# process chunk...
finally:
finally:
file_object.close()
file_object.close()
19. Apriamo in lettura...
F.readlines() -> una lista di stringhe
F.xreadlines() -> iteratore di stringhe
chunk_size = 256
file_object = open('thefile.txt')
file_object = open('binary_file.dat', 'rb')
try:
try:
for line in file_object:
while True:
# process line
chunk = file_object.read(256)
# maybe:
if not chunk:
# print line.rstrip()
break
pass
# process chunk...
finally:
finally:
file_object.close()
file_object.close()
20. Apriamo in lettura...
F.readlines() -> una lista di stringhe
F.xreadlines() -> iteratore di stringhe
F.readline() -> una linea
chunk_size = 256
file_object = open('thefile.txt')
file_object = open('binary_file.dat', 'rb')
try:
try:
for line in file_object:
while True:
# process line
chunk = file_object.read(256)
# maybe:
if not chunk:
# print line.rstrip()
break
pass
# process chunk...
finally:
finally:
file_object.close()
file_object.close()
21. Apriamo in lettura...
F.readlines() -> una lista di stringhe
F.xreadlines() -> iteratore di stringhe
F.readline() -> una linea
F.seek(p), F.tell(P), F.flush()...
chunk_size = 256
file_object = open('thefile.txt')
file_object = open('binary_file.dat', 'rb')
try:
try:
for line in file_object:
while True:
# process line
chunk = file_object.read(256)
# maybe:
if not chunk:
# print line.rstrip()
break
pass
# process chunk...
finally:
finally:
file_object.close()
file_object.close()
23. Un po’ di generatori...
Leggere un chunk alla volta esplicitamente
ripete la logica di iterare su un chunk
24. Un po’ di generatori...
Leggere un chunk alla volta esplicitamente
ripete la logica di iterare su un chunk
def chunk_iterator(filename, chunksize=100):
file_object = open(filename)
try:
while True:
chunk = file_object.read(chunksize)
if not chunk: break
yield chunk
finally:
file_object.close()
for chunk in chunk_iterator('binary_file.dat'):
do_smt_with_chunk(chunk)
25. Un po’ di generatori...
Leggere un chunk alla volta esplicitamente
ripete la logica di iterare su un chunk
def chunk_iterator(filename, chunksize=100):
file_object = open(filename)
try:
while True:
chunk = file_object.read(chunksize)
if not chunk: break
yield chunk
finally:
file_object.close()
for chunk in chunk_iterator('binary_file.dat'):
do_smt_with_chunk(chunk)
Se astraiamo, possiamo scrivere in modo
che funzioni con qualunque oggetto file-like
26. Scrivere
Scrivere é facile:
Metodo F.write(str)
Metodo F.writelines(str)
print >> handle, <0+ expr>
print >> fh, “%d: %s “ % (lineno, line.upper())
Non ci soffermeremo molto
27. File-like objects
Fino a questo modo abbiamo parlato di
“oggetti file”. Di fatto avremmo fatto meglio
a parlare di oggetti “file-like”
Possiamo scrivere codice pensato un file e
dargli in pasto un qualunque oggetto che si
comporti come un file
Es. StringIO, definito in cStringIO (o
StringIO)
28. import sys
from cStringIO import StringIO
def enumerate_lines(fin, fout):
for lineno, line in enumerate(fin):
fout.write('%8d: %s' % (lineno, line))
29. import sys
from cStringIO import StringIO
def enumerate_lines(fin, fout):
for lineno, line in enumerate(fin):
fout.write('%8d: %s' % (lineno, line))
file_object = open(__file__) # __file__ path del modulo
try:
enumerate_lines(file_object, sys.stdout)
finally:
file_object.close()
30. import sys
from cStringIO import StringIO
def enumerate_lines(fin, fout):
for lineno, line in enumerate(fin):
fout.write('%8d: %s' % (lineno, line))
file_object = open(__file__) # __file__ path del modulo
try:
enumerate_lines(file_object, sys.stdout)
finally:
file_object.close()
file_object = open(__file__)
buffer_out = StringIO()
try:
enumerate_lines(file_object, buffer_out)
buffer_out.seek(0)
print buffer_out.read()
finally:
buffer_out.close()
file_object.close()
31. import sys
from cStringIO import StringIO
def enumerate_lines(fin, fout):
for lineno, line in enumerate(fin):
fout.write('%8d: %s' % (lineno, line))
buffer_in = StringIO('''import sys
for line in sys.stdin:
print line.rstrip()
''')
enumerate_lines(buffer_in, sys.stdout)
32. Niente magia...
StringIO non è magico
Rispetta l’interfaccia di un file
Programmare per un’interfaccia e non per
un’implementazione è un cardine della
programmazione OO, e Python la porta al
massimo livello
StringIO si può comportare come un file in
lettura o in scrittura
StringIO.StringIO → lento
cStringIO.StringIO → veloce
34. Pensiamo a Java/C++...
Nei linguaggi con un type-system derivato
da C++ chiediamo che i parametri di una
certa funzione abbiano un dato tipo (o
siano di un sottotipo)
Questo è poco flessibile: programmare per
un interfaccia
Interfacce di Java (vero polimorfismo din.)
Templates di C++ (polimorfismo statico)
Sono soluzioni con problemi
35. Libri, ricerca per titolo
class Book(object):
def __init__(self, title, author):
self.title = title
self.author = author
def find_by_title(seq, title):
for item in seq:
if type(item) == Book: # horrible
if item.title == title:
return item
else:
raise TypeError
def find_by_author(seq, author):
for item in seq:
if type(item) == Book: # horrible
if item.author == author:
return item
else:
raise TypeError
36. Libri, ricerca per titolo
Chiamare su una lista class Book(object):
def __init__(self, title, author):
che contiene un “non self.title = title
self.author = author
libro” lancia eccezione
def find_by_title(seq, title):
for item in seq:
if type(item) == Book: # horrible
if item.title == title:
return item
else:
raise TypeError
def find_by_author(seq, author):
for item in seq:
if type(item) == Book: # horrible
if item.author == author:
return item
else:
raise TypeError
37. Libri, ricerca per titolo
Chiamare su una lista class Book(object):
def __init__(self, title, author):
che contiene un “non self.title = title
self.author = author
libro” lancia eccezione
def find_by_title(seq, title):
Eppure non gestiamo for item in seq:
if type(item) == Book: # horrible
nemmeno sottoclassi if item.title == title:
return item
else:
raise TypeError
def find_by_author(seq, author):
for item in seq:
if type(item) == Book: # horrible
if item.author == author:
return item
else:
raise TypeError
38. Libri, ricerca per titolo
Chiamare su una lista class Book(object):
def __init__(self, title, author):
che contiene un “non self.title = title
self.author = author
libro” lancia eccezione
def find_by_title(seq, title):
Eppure non gestiamo for item in seq:
if type(item) == Book: # horrible
nemmeno sottoclassi if item.title == title:
return item
else:
Peggior codice raise TypeError
possibile, inoltre def find_by_author(seq, author):
stiamo risolvendo un for item in seq:
if type(item) == Book: # horrible
non-problema if item.author == author:
return item
else:
raise TypeError
39. Libri, ricerca per titolo
Chiamare su una lista
che contiene un “non
libro” lancia eccezione
Eppure non gestiamo
nemmeno sottoclassi
Peggior codice
possibile, inoltre
stiamo risolvendo un
non-problema
40. Libri, ricerca per titolo
class Book(object):
def __init__(self, title, author):
self.title = title
self.author = author
def find_by_title(seq, title):
for item in seq:
if isinstance(item, Book): # bad
if item.title == title:
return item
else:
raise TypeError
def find_by_author(seq, author):
for item in seq:
if isinstance(item, Book): # bad
if item.author == author:
return item
else:
raise TypeError
41. Libri, ricerca per titolo
Ora gestiamo class Book(object):
def __init__(self, title, author):
sottoclassi self.title = title
self.author = author
def find_by_title(seq, title):
for item in seq:
if isinstance(item, Book): # bad
if item.title == title:
return item
else:
raise TypeError
def find_by_author(seq, author):
for item in seq:
if isinstance(item, Book): # bad
if item.author == author:
return item
else:
raise TypeError
42. Libri, ricerca per titolo
Ora gestiamo class Book(object):
def __init__(self, title, author):
sottoclassi self.title = title
self.author = author
Eppure noi non usiamo def find_by_title(seq, title):
mai il fatto che la cosa for item in seq:
if isinstance(item, Book): # bad
sia un libro if item.title == title:
return item
else:
raise TypeError
def find_by_author(seq, author):
for item in seq:
if isinstance(item, Book): # bad
if item.author == author:
return item
else:
raise TypeError
43. Libri, ricerca per titolo
Ora gestiamo class Book(object):
def __init__(self, title, author):
sottoclassi self.title = title
self.author = author
Eppure noi non usiamo def find_by_title(seq, title):
mai il fatto che la cosa for item in seq:
if isinstance(item, Book): # bad
sia un libro if item.title == title:
return item
else:
Solo che ha un titolo raise TypeError
def find_by_author(seq, author):
for item in seq:
if isinstance(item, Book): # bad
if item.author == author:
return item
else:
raise TypeError
44. Libri, ricerca per titolo
Ora gestiamo class Book(object):
def __init__(self, title, author):
sottoclassi self.title = title
self.author = author
Eppure noi non usiamo def find_by_title(seq, title):
mai il fatto che la cosa for item in seq:
if isinstance(item, Book): # bad
sia un libro if item.title == title:
return item
else:
Solo che ha un titolo raise TypeError
Solo che ha un autore def find_by_author(seq, author):
for item in seq:
if isinstance(item, Book): # bad
if item.author == author:
return item
else:
raise TypeError
45. Libri, ricerca per titolo
Ora gestiamo class Song(object):
def __init__(self, title, author):
sottoclassi self.title = title
self.author = author
Eppure noi non usiamo def find_by_title(seq, title):
mai il fatto che la cosa for item in seq:
if isinstance(item, Book): # bad
sia un libro if item.title == title:
return item
else:
Solo che ha un titolo raise TypeError
Solo che ha un autore def find_by_author(seq, author):
for item in seq:
Cosa succede se if isinstance(item, Book): # bad
if item.author == author:
abbiamo una classe else:
return item
Song? raise TypeError
46. Libri, ricerca per titolo
Ora gestiamo class Song(object):
def __init__(self, title, author):
sottoclassi self.title = title
self.author = author
Eppure noi non usiamo
mai il fatto che la cosa
sia un libro
Solo che ha un titolo
Solo che ha un autore
Cosa succede se
abbiamo una classe
Song?
47. Libri e canzoni
La cosa più semplice è la
migliore
class Book(object):
def __init__(self, t, a):
self.title = t I programmatori tendono a
self.author = a non scrivere codice a caso
def find_by_title(seq, title):
for item in seq: Abbiamo sempre eccezioni
if item.title == title:
return item
lanciate
def find_by_author(seq, author): I test servono apposta per
for item in seq:
if item.author == author: prendere ‘sta roba
return item
48. E se avessimo film?
I film hanno sempre un titolo, ma non hanno
un autore, bensì un regista
find_by_title dovrebbe funzionare, find_by_author,
no
Interfaccia per Book e Song. E per Movie?
Design Pattern o duplicazione di codice
Ruota quadrata strade per ruote quadrate
Con il duck typing non dobbiamo cambiare
nulla
49. Fail early
Il problema con il nostro approccio si ha
solo nella scrittura di codice exception safe
Il codice può non trovare metodi dopo la
modifica di qualche oggetto non locale
def add(stack):
'''Add top two elements on stack and put the result on the top
>>> s = Stack([1, 2])
>>> add(s)
>>> s.top()
3
>>> add(s)
Traceback (most recent call last):
...
IndexError: pop from empty list'''
op1 = stack.pop(); op2 = stack.pop()
stack.push(op1 + op2)
50. Fail early
Il problema con il nostro approccio si ha
solo nella scrittura di codice exception safe
Il codice può non trovare metodi dopo la
modifica di qualche oggetto non locale
l = [1, 2]
try:
add(l)
except:
print l
# => l = []
51. Fail early
Il problema con il nostro approccio si ha
solo nella scrittura di codice exception safe
Il codice può non trovare metodi dopo la
modifica di qualche oggetto non locale
def add(stack):
'...'
pop = stack.pop
push = stack.push
op1 = pop()
op2 = pop()
push(op1, op2)
52. Fail early
Il problema con il nostro approccio si ha
solo nella scrittura di codice exception safe
Il codice può non trovare metodi dopo la
modifica di qualche oggetto non locale
54. Passaggio di parametri
Nell call-site i parametri
formali vengono legati agli
argomenti attuali della
chiamata
55. Passaggio di parametri
Nell call-site i parametri
formali vengono legati agli
argomenti attuali della
chiamata
I parametri sono quindi
variabili locali nel corpo
della funzione
56. Passaggio di parametri
Nell call-site i parametri Il passaggio è per valore,
formali vengono legati agli ma è il valore
argomenti attuali della dell’etichetta, quello cui
chiamata punta
I parametri sono quindi
variabili locali nel corpo
della funzione
57. Passaggio di parametri
Nell call-site i parametri Il passaggio è per valore,
formali vengono legati agli ma è il valore
argomenti attuali della dell’etichetta, quello cui
chiamata punta
I parametri sono quindi In particolare possiamo
variabili locali nel corpo riassegnare, questo, come
della funzione previsto, non modifica il
codice esterno
58. Passaggio di parametri
Nell call-site i parametri Il passaggio è per valore,
formali vengono legati agli ma è il valore
argomenti attuali della dell’etichetta, quello cui
chiamata punta
I parametri sono quindi In particolare possiamo
def foo(a, b):
variabili locali nel corpo riassegnare, 0:
if a % 2 == questo, come
della funzione previsto,2non+modifica il
a= *a 1
return a + b
codice esterno
x=4
y=4
print foo(x, y)
print x, y
59. Quindi...
def add(L, item):
if item not in L:
L.append(item)
def add2(L, item):
if item not in L:
L.append(item)
L = []
def add3(L, item):
L = []
L.append(item)
60. Quindi...
x = []
def add(L, item): add(x, 4)
if item not in L: print x
L.append(item)
def add2(L, item):
if item not in L:
L.append(item)
L = []
def add3(L, item):
L = []
L.append(item)
61. Quindi...
x = []
def add(L, item): add(x, 4) [4]
if item not in L: print x
L.append(item)
def add2(L, item):
if item not in L:
L.append(item)
L = []
def add3(L, item):
L = []
L.append(item)
62. Quindi...
def add(L, item):
if item not in L:
L.append(item)
def add2(L, item):
if item not in L: x = []
L.append(item) add2(x, 4)
L = [] print x
def add3(L, item):
L = []
L.append(item)
63. Quindi...
def add(L, item):
if item not in L:
L.append(item)
def add2(L, item):
if item not in L: x = []
L.append(item) add2(x, 4) [4]
L = [] print x
def add3(L, item):
L = []
L.append(item)
64. Quindi...
def add(L, item):
if item not in L:
L.append(item)
def add2(L, item):
if item not in L:
L.append(item)
L = []
def add3(L, item):
L = [] x = []
L.append(item) add3(x, 4)
print x
65. Quindi...
def add(L, item):
if item not in L:
L.append(item)
def add2(L, item):
if item not in L:
L.append(item)
L = []
def add3(L, item):
L = [] x = []
L.append(item) add3(x, 4) []
print x
66. Quindi...
def add(L, item):
if item not in L:
L.append(item)
def add2(L, item):
if item not in L:
L.append(item)
L = []
def add3(L, item):
L = []
L.append(item)
67. Optional parameters/
Named parameters
Attenzione a non confondere named
parameters e parametri opzionali, la
sintassi è simile ma:
I parametri opzionali sono parametri formali
specificati nella dichiarazione delle funzione
I named parameters sono parametri attuali
presenti nel call-site della funzione
Quasi ogni funzione può essere chiamata
con named parameters, anche se non
sempre é naturale farlo
68. Parametri Opzionali (1)
def sum(seq, acc=0):
'''Sums elements of seq
acc is the starting value of the sum.'''
for item in seq:
acc += item
return acc
Un parametro opzionale è Quando una chiamata non
specificato come id=exp fornisce un parametro
Il def valuta exp e salva un opzionale, viene usato tale
riferimento al valore fra gli valore
attributi dell’oggetto Attenzione con oggetti
funzione mutabili!
71. Sort
Le liste hanno un metodo L.sort() in-place
L.sort(cmp=None, key=None, reverse=False)
key: funzione applicata ad ogni elemento
che ne da il valore per l’ordinamento
reverse: vogliamo l’ordine inverso?
sorted è un built-in che fa la stessa cosa
applicato ad un iterabile restituisce una
lista
72. Sort
Le liste hanno un metodo L.sort() in-place
L.sort(cmp=None, key=None, reverse=False)
key: funzione applicata ad ogni elemento
che ne da il valore per l’ordinamento
reverse: vogliamo l’ordine inverso?
sorted è un built-in che fa la stessa cosa
applicato ad un iterabile restituisce una
lista
>>> d = dict(a=1, b=2, c=0)
>>> sorted(d, key=d.get) # <= ['c', 'a', 'b']
73. Moduli
Un modulo è un oggetto al quale si
possono legare (e del quale si possono
referenziare) gli attributi
Normalmente il codice di un modulo aname
sta nel file aname.py
Spesso si “confonde” il concetto di modulo
con quello di sorgente python (di fatto ci
sono modi esotici per creare al volo moduli)
Un modulo è anche uno spazio dei nomi
isolato
74. Import
Se foo.py è un file Python nel path di
sistema (equivalente del classpath) con
import foo [as another_name][, ...]
importiamo il file come modulo
Gli statements nel corpo del modulo
vengono eseguiti (def/class son
statements)
Si crea un nuovo spazio dei nomi e gli
attributi del modulo (comprese funzioni e
classi) sono accessibili come foo.name
75. Esempio
simple_module.py
>>> import simple_module
class Spam(object):
>>> simple_module.Spam()
pass
<simple_module.Spam ...>
>>> e = simple_module.Eggs()
class Eggs(object):
>>> simple_module.menu(e)
pass
[<simple_module.Eggs ...>,
<simple_module.Spam ...>]
def menu(course):
>>> simple_module.base_menu
return [course,Spam()]
[<simple_module.Eggs ...>,
<simple_module.Spam ...>]
base_menu = menu(Eggs())
76. Import esegue sempre
tutto?
Il fatto che import esegua sempre tutto il
codice può essere seccante in molte
circostanze
Facciamo un esempio quasi reale.
Scriviamo un semplice programma che usa
SQLite per mantenere una collezione di
libri
Semplice vuole dire una tabella e non
gestiamo come si deve chiave, inserimenti
multipli etc (quella è roba di SQL
77. import os, sys
import sqlite3
class Book(object):
'Represents a Book in our model'
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return '%s, %s' % (self.title, self.author)
class Library(object):
'''Represents the library in our model.'''
def __init__(self, db_name):
self.open_db(db_name)
def open_db(self, fname):
'''Open specified db.
Create it if it does not exist'''
if not os.path.exists(fname):
self._initialize_db(fname)
self.conn = sqlite3.connect(fname)
def _initialize_db(self, fname):
conn = sqlite3.connect(fname)
c = conn.cursor()
c.execute('''create table books(title string, author string)''')
conn.commit()
c.close()
78. ... # continua da Library
def print_books(self, out=sys.stdout):
'''Print all the book to supplied output.
Default: sys.stdout'''
c = self.conn.cursor()
c.execute('select title, author from books')
for author, title in c:
print >> out, author, title
def books(self):
'''Return a list of all books in memory'''
c = self.conn.cursor()
c.execute('select title, author from books')
return [Book(author, title) for author, title in c]
def find(self, author=None, title=None):
'''Find book by author or title'''
c = self.conn.cursor()
if author:
c.execute('select title, author from books where author like ?',
(author, ))
elif title:
c.execute('select title, author from books where title like ?',
(title, ))
else:
raise Exception
return [Book(author, title) for author, title in c]
79. lib = Library('books.db')
lib.insert(Book('Alex Martelli', 'Python in a Nutshell'))
lib.insert(Book('Marco Beri', 'Python'))
lib.insert(Book('Marco Beri', 'Sviluppare applicazioni web con Django'))
Abbiamo definito due classi
Il codice in questa slide è direttamente nel
corpo del modulo
Viene eseguito ad ogni import
Noi vogliamo che sia eseguito solo quando
il tutto è chiamato da command line
80. Abbiamo definito due classi
Il codice in questa slide è direttamente nel
corpo del modulo
Viene eseguito ad ogni import
Noi vogliamo che sia eseguito solo quando
il tutto è chiamato da command line
if __name__ == '__main__':
lib = Library('books.db')
lib.insert(Book('Alex Martelli', 'Python in a Nutshell'))
lib.insert(Book('Marco Beri', 'Python'))
lib.insert(Book('Marco Beri', 'Sviluppare applicazioni web con Django'))
81. Abbiamo definito due classi
Il codice in questa slide è direttamente nel
corpo del modulo
Viene eseguito ad ogni import
Noi vogliamo che sia eseguito solo quando
il tutto è chiamato da command line
if __name__ == '__main__':
lib = Library('books.db')
lib.insert(Book('Alex Martelli', 'Python in a Nutshell'))
lib.insert(Book('Marco Beri', 'Python'))
lib.insert(Book('Marco Beri', 'Sviluppare applicazioni web con Django'))
__name__ normalmente vale il nome del modulo, vale
‘__main__’ quando il modulo è chiamato come progr.
82. NOTA A MARGINE
Abbiamo definito due classi
In un progetto Python vero, probabilmente
Il codice in questa slide è direttamente nel ORM
si vorrebbe usare SQLAlchemy, come
corpo del modulo
o semplicemente come astrazione dal DB.
Viene eseguito ad ogni import
Noi vogliamoèche siausato e comodo, sebbene
Di fatto molto eseguito solo quando
fuori dallo scopecommand presentazione
il tutto è chiamato da di questa line
Con Django invece probabilmente si userebbe
if __name__ == '__main__':
lib = Library('books.db')
il suo ORM. Il modulo SQLite è comunque
lib.insert(Book('Alex Martelli', 'Python in a Nutshell'))
lib.insert(Book('Marco Beri', 'Python'))
comodo da avere pronto.
lib.insert(Book('Marco Beri', 'Sviluppare applicazioni web con Django'))
__name__ normalmente vale il nome del modulo, vale
‘__main__’ quando il modulo è chiamato come progr.
83. Reload
Se si è nell’interprete interattivo, è possibile
ricaricare un modulo con l’espressione
reload(nomemodulo)
Il modulo doveva essere stato importato con lo
statement import
reload non è una soluzione pronta per fare hot-
swap di codice, risolve il problema di facilitare
lo sviluppo con l’interprete interattivo
L’hot-swap non è un argomento di frontiera in
Python, sul Cookbook ci sono diverse ricette
84. Packages
Python offre anche packages, ovvero
“moduli che contengono altri moduli”
Es. os.path.join(...): path é un sottomodulo
Non mostriamo qui come farli, a livello base
si ha una dir con un __init__.py e altri
moduli dentro
Anche un file .zip può comportarsi come
un package
85. Testing 39
Nella programmazione odierna il testing è
essenziale
Nessun gruppo pensa di poter sviluppare
un software minimamente complesso
senza usare abbondantemente i test
Ormai sono standard metodologie come
TDD (test driven developement) anche al di
fuori di ambienti agili
Python è TDD ready
86. Doctest (1)
Il modo più semplice di scrivere test in
Python è utilizzando le stesse docstrings
def concat_all(seq, acc=None):
'''Concatenate a list of strings.
>>> concat_all([[1, 2], [3], [4, 5]])
[1, 2, 3, 4, 5]
>>> concat_all([['a', 'b'], ['c']])
['a', 'b', 'c']
'''
acc = acc or []
for iterable in seq:
acc.extend(iterable)
return acc
87. Doctest (2)
'''Concatenate a list of strings.
>>> concat_all([[1, 2], [3], [4, 5]])
[1, 2, 3, 4, 5]
>>> concat_all([['a', 'b'], ['c']])
['a', 'b', 'c']
'''
Nella documentazione ci sono esempi,
“come presi dall’inteprete python
interattivo”
Potremmo verificare che l’output
presentato nell’esempio è quello
88. Doctest (3)
Per fare questo, basta mettere in fondo al
modulo:
if __name__ == '__main__':
import doctest
doctest.testmod()
Doctest è comodo, arricchisce la
documentazione di esempi (eseguibili), ma
non si presta ad unit-testing su larga scala
Guarda il modulo standard unittest...
... o ancora meglio nosetest
89. Lint
Scrivere assolutamente i test...
... ma anche un analizzatore statico può
essere comodo
Pyflakes
Pylint
Catturano alcuni errori staticamente
(leggere variabili non assegnate, usare
attributi non presenti)
Danno metriche sulla qualità del codice
90. Libreria standard
Imparare a guardare nella libreria standard
con pydoc. Pochi (~2%) esempi:
zlib: crc, (de)compressione di buffer
collections (deque)
ctypes: facile interfacciamento dinamico con
codice C
cmd: creazione di UI testuali interattive
copy: protocolli di copia
bisect (ricerca binaria), heapq (priority
91. Socket UDP: server
import os
import socket
def server(port=8081):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", port))
print "Waiting on port", port
try:
while True:
data, addr = s.recvfrom(1024)
print "Received:", data, "from", addr
except KeyboardInterrupt:
print "Quitting..."
finally:
s.close()
93. Socket UDP: client
def client(port=8081, host='localhost'):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto('Hello, [remote] world!', (host, port))
def improved_client(msg, port=8081, host='localhost', bufsize=1024):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while msg:
bytes_sent = s.sendto(msg[:bufsize], (host, port))
msg = msg[bytes_sent:]
if __name__ == '__main__':
pid = os.fork()
if pid:
server()
else:
client()
client()
improved_client('WOW! IT WORKS')
94. SNTP Client
import socket, struct, sys, time
TIME1970 = 2208988800L
client = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM)
data = 'x1b' + 47 * '0'
client.sendto(data, (sys.argv[1], 123))
data, address = client.recvfrom(1024)
if data:
print 'Response received from:', address
t = struct.unpack('!12I', data)[10]
t -= TIME1970
print 'tTime=%s' % time.ctime(t)
95. SNTP Client
import socket, struct, sys, time
TIME1970 = 2208988800L
client = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM)
% python sntp_client.py time.euro.apple.com
data = 'x1b' + 47 * '0'
client.sendto(data, (sys.argv[1], ('17.72.255.12', 123)
Response received from: 123))
Time=Sat Apr 4 19:46:09 2009
data, address = client.recvfrom(1024)
if data:
print 'Response received from:', address
t = struct.unpack('!12I', data)[10]
t -= TIME1970
print 'tTime=%s' % time.ctime(t)
96. Serializzazione
Serializzare un oggetto in modo portabile è
in generale un problema non banale
In C++ è particolarmente seccante per
questioni di endianness e dimensione dei
tipi
In Python è fornito dalla libreria standard:
Pickle (cPickle): general purpose
Marshal: funziona con tipi builtin e oggetti
codice (usato anche per i compilati python)
97. cPickle
cPickle è l’implementazione in C
Due metodi principali, load e dump
Funziona con qualunque oggetto file-like:
dump chiede write(s)
load chiede read(d) e readline()
Gestisce cicli, referenze multiple, etc...
Possiamo usare socket al posto di file?
sock.makefile([mode[, bufsize]])
98. # -*- coding: utf-8 -*-
from __future__ import with_statement
import cPickle as pickle
class Person(object):
def __init__(self, name, surname):
self.name = name
self.surname = surname
def marries(self, person):
self.spouse = person
person.spouse = self
def __str__(self):
base = '%s %s' % (self.name, self.surname)
if hasattr(self, 'spouse'):
base += '-> %s %s' % (self.spouse.name,
self.spouse.surname)
return base
luthien = Person('Lúthien', 'Tinúviel')
beren = Person('Beren', 'Erchamion')
beren.marries(luthien)
print beren
print luthien
99. with open('couple.dat', 'wb') as f:
pickle.dump(beren, f, pickle.HIGHEST_PROTOCOL)
# pickle.dump(luthien, f, pickle.HIGHEST_PROTOCOL)
del beren
del luthien
with open('couple.dat', 'rb') as f:
beren = pickle.load(f)
# luthien = pickle.load(f)
luthien = beren.spouse
print beren
print luthien
101. if __name__ == '__main__':
pid = os.fork()
if pid:
for obj in Server('', 9876):
print obj
if obj == 'quit':
sys.exit(0)
else:
luthien = Person('Lúthien', 'Tinúviel')
beren = Person('Beren', 'Erchamion')
beren.marries(luthien)
cl = Client('', 9876)
cl.send(beren)
cl.send(luthien)
cl.send('quit')
Attenzione: Python offre classi come TPCServer,
UDPServer, ThreadingTCPServer,
ThreadingUDPServer, SimpleHTTPServer,
CGIHTTPServer.
102. if __name__ == '__main__':
pid = os.fork()
if pid:
for obj in Server('', 9876):
print obj
if obj == 'quit':
sys.exit(0)
else:
luthien = Person('Lúthien', 'Tinúviel')
beren = Person('Beren', 'Erchamion')
beren.marries(luthien)
cl = Client('', 9876)
cl.send(beren)
cl.send(luthien)
cl.send('quit')
Attenzione: Python offre classi come TPCServer,
UDPServer, ThreadingTCPServer,
ThreadingUDPServer, SimpleHTTPServer,
CGIHTTPServer.
NON DIMENTICARE TWISTED
103. Python e POSIX
Python offre numerose funzioni familiari a
chi viene dalla programmazione POSIX
Molte sono in os, qualcuna in sys, socket,
time (time.sleep), socket, mmaps, popen2
In generale conoscere la libreria standard
aiuta (Python in a Nutshell o pydoc)
Non sempre però è il modo migliore:
Ci sono librerie di livello più alto
104. subprocess
Subprocess è una libreria di alto livello che
funziona su Unix (e altro) e Win
Serve per creare figli e maneggiare il loro
input-output in maniera facile
class Popen(args, bufsize=0, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=False, shell=False,
cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0):
105. Pexpect
Se l’input/output fra padre e figlio è
particolarmente interattivo:
pexpect
Es. il figlio è pensato per essere pilotato da
un umano
106. Client di rete in Python
urlparse: parsing di url (urljoin, urlsplit)
urllib: leggere dati da http/https/ftp/file
urllib2: sopra-insieme di urllib, un po’ più
complesso e più ricco
poplib: interrogare server POP3
imaplib: interrogare server IMAP4
smtplib: inviare mail con SMTP
httplib/ftplib
nntplib: leggere e inviare messaggi Usenet
107. email
Il package email è una delle
implementazioni più ricche e fedeli degli
standard relativi
Rende semplice costruire messaggi di
testo semplice come complessi messaggi
multipart
Specifico e poco generale: ricordatevi che
esiste, se vi serve esempi su web + doc
108. Server side modules
Scriviamo un gestore (“handler”) che
contiene il codice per implementare il
protocollo
Creiamo un server cui passiamo l’handler
Il server gestisce i dettagli, noi il protocollo
Server prefabbricati:
TCPServer ForkingUDPServer
UDPServer ThreadingTCPServe
r
ForkingTCPServer
109. Parsing XML
Libreria standard
SaX
DOM (minidom, pulldom)
BeautifulSoup
Parserizza anche XML e HTML mal formato
ElementTree
Interfaccia molto pythonica
111. Web
cgi (sconsigliato)/fastcgi
WSGI: modulo di medio livello per interfacciarsi con i
server
Django: framework completo, molto usato
Nevow: framework completo basato su Twisted
Zope2/3: “mega framework”
Google App Engine (+web.py ev. Django)
web.py: modulo minimalista, facile comprensione
112. Web.py
import web
urls = (
'/(.*)', 'hello'
)
app = web.application(urls, globals())
class hello:
def GET(self, name):
if not name:
name = 'world'
return 'Hello, ' + name + '!'
if __name__ == "__main__":
app.run()
113. ipython
Oltre all’interprete interattivo standard, si
può installare ipython, una shell python
Help più esteso
Funzioni di introspezione e completamento
dinamico del codice
...