SlideShare uma empresa Scribd logo
1 de 113
Approfondimenti su
Python
Enrico Franchi
enrico.franchi@gmail.com
franchi@cs.unipr.it
DOMANDE?
RIGIDO
     O
FLESSIBILE?
CAMBIARE
     LE
FONDAMENTA?
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
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()
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
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
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__)
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)
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.
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'
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'))
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
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
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()
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()
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()
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()
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()
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()
Un po’ di generatori...
Un po’ di generatori...
 Leggere un chunk alla volta esplicitamente
 ripete la logica di iterare su un chunk
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)
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
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
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)
import sys
from cStringIO import StringIO

def enumerate_lines(fin, fout):
  for lineno, line in enumerate(fin):
     fout.write('%8d: %s' % (lineno, line))
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()
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()
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)
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
... solo Duck Typing
import time

class DateWriter(object):
   def __init__(self, handle_or_name, mode='w'):
      try:
         handle_or_name.write
         self.fh = handle_or_name
      except AttributeError:
         self.fname = handle_or_name
         # check mode is write mode...
         self.fh = open(self.fname, mode)

  def write(self, string):
    return len(self.fh.write('[%24s] %s' % 
            (time.ctime(), string)))
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
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
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
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
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
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
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
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
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
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
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
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
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?
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
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
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)
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 = []
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)
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
Passaggio di parametri
Passaggio di parametri
Nell call-site i parametri
formali vengono legati agli
argomenti attuali della
chiamata
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
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
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
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
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)
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)
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)
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)
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)
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
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
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)
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
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!
Parametri Opzionali (2)

                          SBAGLIATO

    def concat_all(seq, acc=[]):
      for iterable in seq:
         acc.extend(iterable)
      return acc

    print concat_all([[1, 2], [3], [4, 5]])
    # => [1, 2, 3, 4, 5]
    print concat_all([['a', 'b'], ['c']])
    # => [1, 2, 3, 4, 5, 'a', 'b', 'c']
Parametri Opzionali (2)

                             GIUSTO

    def concat_all(seq, acc=None):
      acc = acc or []
      for iterable in seq:
         acc.extend(iterable)
      return acc

    print concat_all([[1, 2], [3], [4, 5]])
    # => [1, 2, 3, 4, 5]
    print concat_all([['a', 'b'], ['c']])
    # => [1, 2, 3, 4, 5, 'a', 'b', 'c']
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
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']
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
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
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())
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
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()
... # 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]
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
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'))
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.
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.
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
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
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
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
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
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
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
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
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()
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:]
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')
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)
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)
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)
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]])
# -*- 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
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
import os, sys, socket

class Server(object):
   def __init__(self, host, port):
      self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      self.sock.bind((host, port))
      self.sock.listen(5)

  def __iter__(self):
    while True:
       connection, address = self.sock.accept()
       fh = connection.makefile('rb')
       while True:
          obj = pickle.load(fh)
          yield obj



class Client(object):
   def __init__(self, host, port):
      self.host = host
      self.port = port
      self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      self.sock.connect((self.host, self.port))
      self.fh = self.sock.makefile('wb')

  def send(self, obj):
    pickle.dump(obj, self.fh)
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.
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
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
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):
Pexpect
Se l’input/output fra padre e figlio è
particolarmente interattivo:
  pexpect
Es. il figlio è pensato per essere pilotato da
un umano
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
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
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
Parsing XML
Libreria standard
  SaX
  DOM (minidom, pulldom)
BeautifulSoup
  Parserizza anche XML e HTML mal formato
ElementTree
  Interfaccia molto pythonica
Interfacce grafiche
 Tkinter
   Multipiattaforma, libreria standard,
   bruttarello
 Qt
   Piattaforma ricchissima, cross-platform, ++
 wx
   Cross-platform, ... [non inneschiamo
   flames]
 GTK, Cocoa, WinAPI, + .Net con
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
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()
ipython
Oltre all’interprete interattivo standard, si
può installare ipython, una shell python
  Help più esteso
  Funzioni di introspezione e completamento
  dinamico del codice
  ...

Mais conteúdo relacionado

Mais de rik0

Complex and Social Network Analysis in Python
Complex and Social Network Analysis in PythonComplex and Social Network Analysis in Python
Complex and Social Network Analysis in Pythonrik0
 
Pydiomatic
PydiomaticPydiomatic
Pydiomaticrik0
 
Pycrashcourse4.0 pdfjam
Pycrashcourse4.0 pdfjamPycrashcourse4.0 pdfjam
Pycrashcourse4.0 pdfjamrik0
 
Twcrashcourse
TwcrashcourseTwcrashcourse
Twcrashcourserik0
 
Pyimproved again
Pyimproved againPyimproved again
Pyimproved againrik0
 
Pycrashcourse3.1
Pycrashcourse3.1Pycrashcourse3.1
Pycrashcourse3.1rik0
 
Pycrashcourse3.0
Pycrashcourse3.0Pycrashcourse3.0
Pycrashcourse3.0rik0
 
Pycrashcourse2.0
Pycrashcourse2.0Pycrashcourse2.0
Pycrashcourse2.0rik0
 
Pycrashcourse
PycrashcoursePycrashcourse
Pycrashcourserik0
 

Mais de rik0 (9)

Complex and Social Network Analysis in Python
Complex and Social Network Analysis in PythonComplex and Social Network Analysis in Python
Complex and Social Network Analysis in Python
 
Pydiomatic
PydiomaticPydiomatic
Pydiomatic
 
Pycrashcourse4.0 pdfjam
Pycrashcourse4.0 pdfjamPycrashcourse4.0 pdfjam
Pycrashcourse4.0 pdfjam
 
Twcrashcourse
TwcrashcourseTwcrashcourse
Twcrashcourse
 
Pyimproved again
Pyimproved againPyimproved again
Pyimproved again
 
Pycrashcourse3.1
Pycrashcourse3.1Pycrashcourse3.1
Pycrashcourse3.1
 
Pycrashcourse3.0
Pycrashcourse3.0Pycrashcourse3.0
Pycrashcourse3.0
 
Pycrashcourse2.0
Pycrashcourse2.0Pycrashcourse2.0
Pycrashcourse2.0
 
Pycrashcourse
PycrashcoursePycrashcourse
Pycrashcourse
 

Pyimproved

  • 3. RIGIDO O FLESSIBILE?
  • 4. CAMBIARE LE FONDAMENTA?
  • 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()
  • 22. Un po’ di generatori...
  • 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
  • 33. ... solo Duck Typing import time class DateWriter(object): def __init__(self, handle_or_name, mode='w'): try: handle_or_name.write self.fh = handle_or_name except AttributeError: self.fname = handle_or_name # check mode is write mode... self.fh = open(self.fname, mode) def write(self, string): return len(self.fh.write('[%24s] %s' % (time.ctime(), string)))
  • 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!
  • 69. Parametri Opzionali (2) SBAGLIATO def concat_all(seq, acc=[]): for iterable in seq: acc.extend(iterable) return acc print concat_all([[1, 2], [3], [4, 5]]) # => [1, 2, 3, 4, 5] print concat_all([['a', 'b'], ['c']]) # => [1, 2, 3, 4, 5, 'a', 'b', 'c']
  • 70. Parametri Opzionali (2) GIUSTO def concat_all(seq, acc=None): acc = acc or [] for iterable in seq: acc.extend(iterable) return acc print concat_all([[1, 2], [3], [4, 5]]) # => [1, 2, 3, 4, 5] print concat_all([['a', 'b'], ['c']]) # => [1, 2, 3, 4, 5, 'a', 'b', 'c']
  • 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()
  • 92. 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:]
  • 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
  • 100. import os, sys, socket class Server(object): def __init__(self, host, port): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.bind((host, port)) self.sock.listen(5) def __iter__(self): while True: connection, address = self.sock.accept() fh = connection.makefile('rb') while True: obj = pickle.load(fh) yield obj class Client(object): def __init__(self, host, port): self.host = host self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((self.host, self.port)) self.fh = self.sock.makefile('wb') def send(self, obj): pickle.dump(obj, self.fh)
  • 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
  • 110. Interfacce grafiche Tkinter Multipiattaforma, libreria standard, bruttarello Qt Piattaforma ricchissima, cross-platform, ++ wx Cross-platform, ... [non inneschiamo flames] GTK, Cocoa, WinAPI, + .Net con
  • 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 ...

Notas do Editor

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n
  77. \n
  78. \n
  79. \n
  80. \n
  81. \n
  82. \n
  83. \n
  84. \n
  85. \n
  86. \n
  87. \n
  88. \n
  89. \n
  90. \n
  91. \n
  92. \n
  93. \n
  94. \n
  95. \n
  96. \n
  97. \n
  98. \n
  99. \n
  100. \n
  101. \n
  102. \n
  103. \n
  104. \n
  105. \n
  106. \n
  107. \n
  108. \n
  109. \n
  110. \n
  111. \n
  112. \n
  113. \n