Subsections


4. Di più sugli strumenti di controllo del flusso

Oltre all'istruzione while appena introdotta, Python riconosce le solite istruzioni di controllo del flusso presenti in altri linguaggi, con qualche particolarità.


4.1 L'Istruzione if

Forse il tipo di istruzione più conosciuta è if. Per esempio:

>>> x = int(raw_input("Introdurre un numero: "))
>>> if x < 0:
...      x = 0
...      print 'Numero negativo cambiato in zero'
... elif x == 0:
...      print 'Zero'
... elif x == 1:
...      print 'Uno'
... else:
...      print 'Più di uno'
...

Possono essere presenti o meno, una o più parti elif, e la parte else è facoltativa. La parola chiave `elif' è un'abbreviazione di `else if', e serve ad evitare un eccesso di indentazioni. Una sequenza if ... elif ... elif ... sostituisce le istruzioni switch o case che si trovano in altri linguaggi.


4.2 L'Istruzione for

L'istruzione for di Python differisce un po' da quella a cui si è abituati in C o Pascal. Piuttosto che iterare sempre su una progressione aritmetica (come in Pascal), o dare all'utente la possibilità di definire sia il passo iterativo che la condizione di arresto (come in C), in Python l'istruzione forcompie un'iterazione sugli elementi di una qualsiasi sequenza (p.e. una lista o una stringa), nell'ordine in cui appaiono nella sequenza. Ad esempio (senza voler fare giochi di parole!):

>>> # Misura la lunghezza di alcune stringhe:
... a = ['gatto', 'finestra', 'defenestrare']
>>> for x in a:
...     print x, len(x)
...
gatto 5
finestra 8
defenestrare 12

Non è prudente modificare all'interno del ciclo la sequenza su cui avviene l'iterazione (può essere fatto solo per tipi di sequenze mutabili, come le liste). Se è necessario modificare la lista su cui si effettua l'iterazione, p.e. duplicare elementi scelti, si deve iterare su una copia. La notazione a fette rende questo procedimento particolarmente conveniente:

>>> for x in a[:]: # fa una copia tramite affettamento dell'intera lista
...    if len(x) > 6: a.insert(0, x)
...
>>> a
['defenestrare', 'gatto', 'finestra', 'defenestrare']


4.3 La funzione range()

Se è necessario iterare su una successione di numeri, viene in aiuto la funzione built-in range(), che genera liste contenenti progressioni aritmetiche, p.e.:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

L'estremo destro passato alla funzione non fa mai parte della lista generata; range(10) genera una lista di 10 valori, esattamente gli indici leciti per gli elementi di una sequenza di lunghezza 10. È possibile far partire l'intervallo da un altro numero, o specificare un incremento diverso (persino negativo, talvolta è chiamato `step' (NdT: `passo'):

>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]

Per effettuare un'iterazione sugli indici di una sequenza, si usino in combinazione range() e len() come segue:

>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
...     print i, a[i]
... 
0 Mary
1 had
2 a
3 little
4 lamb


4.4 Le istruzioni break e continue e la clausola else nei cicli

L'istruzione break, come in C, esce immediatamente dal ciclo for o while più interno che la racchiude.

L'istruzione continue, anch'essa presa a prestito dal C, prosegue con l'iterazione seguente del ciclo.

Le istruzioni di ciclo possono avere una clausola else che viene eseguita quando il ciclo termina per esaurimento della lista (con for) o quando la condizione diviene falsa (con while), ma non quando il ciclo è terminato da un'istruzione break. Questo viene esemplificato nel ciclo seguente, che ricerca numeri primi:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'è uguale a', x, '*', n/x
...             break
...     else:
...         # Il ciclo scorre la sequenza senza trovare il fattore
...         print n, 'è un numero primo'
... 
2 è un numero primo
3 è un numero primo
4 è uguale a 2 * 2
5 è un numero primo
6 è uguale a 2 * 3
7 è un numero primo
8 è uguale a 2 * 4
9 è uguale a 3 * 3


4.5 L'istruzione pass

L'istruzione pass non fa nulla. Può essere usata quando un'istruzione è necessaria per sintassi ma il programma non richiede venga svolta alcuna azione. Per esempio:

>>> while True:
...       pass # In attesa di un interrupt da tastiera
...


4.6 Definizione di funzioni

Possiamo creare una funzione che scrive la serie di Fibonacci fino ad un limite arbitrario:

>>> def fib(n):    # scrive la serie di Fibonacci fino a n
...     "Stampa una serie di Fibonacci fino a n"
...     a, b = 0, 1
...     while b < n:
...         print b,
...         a, b = b, a+b
...
>>> # Adesso si invochi la funzione appena definita:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

La parola chiave def introduce una definizione di funzione. Dev'essere seguita dal nome della funzione e dalla lista dei parametri formali racchiusa tra parentesi. Le istruzioni che compongono il corpo della funzione iniziano alla riga successiva, e devono essere indentate. La prima istruzione del corpo della funzione può essere facoltativamente una stringa di testo: è la stringa di documentazione della funzione o docstring.

Ci sono degli strumenti di sviluppo che usano le docstring per produrre automaticamente documentazione stampata, o per permettere all'utente una navigazione interattiva attraverso il codice. È buona abitudine includere le docstring nel proprio codice, si cerchi quindi di farci l'abitudine.

L'esecuzione di una funzione introduce una nuova tabella di simboli, usata per le variabili locali della funzione. Più precisamente, tutti gli assegnamenti di variabili in una funzione memorizzano il valore nella tabella dei simboli locale, laddove i riferimenti a variabili cercano prima nella tabella dei simboli locale, poi nella tabella dei simboli globale, e quindi nella tabella dei nomi built-in. Di conseguenza ad una variabile globale non può essere assegnato direttamente un valore all'interno di una funzione (a meno che non compaia in un'istruzione global), malgrado ci si possa riferire alla variabile.

I parametri attuali (argomenti) di una chiamata a funzione vengono introdotti nella tabella dei simboli locale della funzione al momento della chiamata; perciò gli argomenti sono passati usando una chiamata per valore (dove il valore è sempre un riferimento ad un oggetto, non il valore dell'oggetto). 4.1 Quando una funzione chiama un'altra funzione, viene creata una nuova tabella locale dei simboli per tale chiamata.

Una definizione di funzione introduce il nome della funzione nella tabella dei simboli corrente. Il valore del nome della funzione è di un tipo che viene riconosciuto dall'interprete come funzione definita dall'utente. Tale valore può essere assegnato a un altro nome che quindi può venire anch'esso usato come una funzione. Questo serve come meccanismo generale di ridenominazione:

>>> fib
<function object at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

Si potrebbe obiettare che fib non è una funzione ma una procedura. In Python, come in C, le procedure sono semplicemente funzioni che non restituiscono un valore. In effetti, tecnicamente parlando, le procedure restituiscono comunque un valore, quantunque piuttosto inutile. Tale valore è chiamato None (è un nome built-in). La scrittura del valore None è di norma soppressa dall'interprete nel caso si tratti dell'unico valore scritto. Lo si può vedere se si vuole:

>>> print fib(0)
None

È facile scrivere una funzione che restituisce una lista dei numeri delle serie di Fibonacci anziché stamparli:

>>> def fib2(n): # restituisce la serie di Fibonacci fino a n
...     """Restituisce una lista contenente la serie di Fibonacci fino a n"""
...     result = []
...     a, b = 0, 1
...     while b < n:
...         result.append(b)    # vedi sotto
...         a, b = b, a+b
...     return result
... 
>>> f100 = fib2(100)    # chiama la funzione
>>> f100                # scrive il risultato
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Questo esempio, come al solito, fornisce un esempio di alcune nuove funzionalità di Python:


4.7 Di più sulla definizione di funzioni

È anche possibile definire funzioni con un numero variabile di argomenti. Ci sono tre forme, che possono essere combinate.


4.7.1 Valori predefiniti per gli argomenti

La forma più utile è specificare un valore predefinito per uno o più argomenti. In questo modo si crea una funzione che può essere chiamata con un numero di argomenti minore rispetto alla definizione di quanti ne sono stati consentiti. Per esempio:

def ask_ok(prompt, retries=4, complaint='Sì o no, grazie!'):
    while True:
        ok = raw_input(prompt)
        if ok in ('s', 'si', 'sì'): return True
        if ok in ('n', 'no', 'nop', 'nope'): return False
        retries = retries - 1
        if retries < 0: raise IOError, 'refusenik user'
        print complaint

Questa funzione può essere chiamata così: ask_ok('Vuoi davvero uscire?') o così: ask_ok('Devo sovrascrivere il file?', 2).

Questo esempio introduce anche la parola chiave in. Questo esempio verifica se una sequenza contiene o meno un certo valore.

I valori predefiniti sono valutati al momento della definizione della funzione nella definizione dello spazio dei nomi, così che per esempio:

i = 5

def f(arg=i):
    print arg

i = 6
f()

Stamperà 5.

Avviso importante: Il valore predefinito viene valutato una volta sola. Ciò fa sì che le cose siano molto diverse quando si tratta di un oggetto mutabile come una lista, un dizionario o istanze di più classi. A esempio, la seguente funzione accumula gli argomenti ad essa passati in chiamate successive:

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

Che stamperà:

[1]
[1, 2]
[1, 2, 3]

Se si desidera che il valore predefinito non venga condiviso tra chiamate successive, si può scrivere la funzione in questo modo:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L


4.7.2 Argomenti a parola chiave

Le funzioni possono essere chiamate anche usando argomenti a parola chiave nella forma "parolachiave = valore". Per esempio la funzione seguente:

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print "-- This parrot wouldn't", action,
    print "if you put", voltage, "Volts through it."
    print "-- Lovely plumage, the", type
    print "-- It's", state, "!"

Potrebbe essere chiamata in uno qualsiasi dei seguenti modi:

parrot(1000)
parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')

Invece le chiamate seguenti non sarebbero valide:

parrot()                     #  manca un argomento necessario
parrot(voltage=5.0, 'dead')  #  argomento non a parola chiave seguito
                             #+ da una parola chiave
parrot(110, voltage=220)     #  valore doppio per un argomento
parrot(actor='John Cleese')  #  parola chiave sconosciuta

In generale, una lista di argomenti deve avere un numero qualunque di argomenti posizionali seguiti da zero o più argomenti a parola chiave, ove le parole chiave devono essere scelte tra i nomi dei parametri formali. Non è importante se un parametro formale ha un valore predefinito o meno. Nessun argomento deve ricevere un valore più di una volta -- in una medesima invocazione non possono essere usati come parole chiave nomi di parametri formali corrispondenti ad argomenti posizionali. Ecco un esempio di errore dovuto a tale restrizione:

>>> def function(a):
...     pass
... 
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'

Quando è presente un parametro formale finale nella forma **nome, esso riceve un dizionario dizionario contenente tutti gli argomenti a parola chiave la cui parola chiave non corrisponde a un parametro formale. Ciò può essere combinato con un parametro formale della forma *nome (descritto nella prossima sottosezione) che riceve una tupla contenente gli argomenti posizionali in eccesso rispetto alla lista dei parametri formali (*nome deve trovarsi prima di **nome). Per esempio, se si definisce una funzione come la seguente:

def cheeseshop(kind, *arguments, **keywords):
    print "-- Do you have any", kind, '?'
    print "-- I'm sorry, we're all out of", kind
    for arg in arguments: print arg
    print '-'*40
    keys = keywords.keys()
    keys.sort()
    for kw in keys: print kw, ':', keywords[kw]

Essa potrà venire invocata così:

cheeseshop('Limburger', "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           client='John Cleese',
           shopkeeper='Michael Palin',
           sketch='Cheese Shop Sketch')

Naturalmente stamperà:

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

Si noti che il metodo sort() applicato alla lista dei nomi degli argomenti delle parole chiave viene chiamato prima di stampare il contenuto del dizionario delle parole chiave; se questo non fosse fatto, l'ordine con il quale verrebbero stampati gli argomenti non sarebbe definito.


4.7.3 Liste di argomenti arbitrari

Infine, l'opzione usata meno di frequente consiste nello specificare che una funzione può essere chiamata con un numero arbitrario di argomenti. Tali argomenti verranno incapsulati in una tupla. Prima degli argomenti in numero variabile possono esserci zero o più argomenti normali.

def fprintf(file, format, *args):
    file.write(format % args)


4.7.4 Suddividere gli argomenti di una lista

La situazione inversa capita quando vi sono argomenti presenti nella lista o nella tupla ma è necessario che siano suddivisi perché la chiamata di una funzione richiede argomenti posizionali ben distinti. Per istanza la funzione range() si aspetta gli argomenti start e stop separati. Se non sono disponibili separatamente si può sempre scrivere una funzione con l'operatore * per suddividere gli argomenti di una lista o di una tupla:

>>> range(3, 6)             #  la canonica chiamata con argomenti
                            #+ separati 
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args)            #  chiamata con argomenti suddivisi,
                            #+ provenienti da una lista
[3, 4, 5]


4.7.5 Forme lambda

A seguito di numerose richieste, a Python sono state aggiunte alcune (poche) funzionalità che si trovano comunemente nei linguaggi di programmazione funzionale e in Lisp. Con la parola chiave lambda possono essere create piccole funzioni senza nome. Ecco una funzione che ritorna la somma dei suoi due argomenti: ""lambda a, b: a+b". Le forme lambda possono essere usate ovunque siano richiesti oggetti funzione. Esse sono sintatticamente ristrette ad una singola espressione. Dal punto di vista semantico, sono solo un surrogato di una normale definizione di funzione. Come per le definizioni di funzioni annidate, le forme lambda possono riferirsi a variabili dallo scope (NdT: ambito di visibilità) che le contiene:

>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43


4.7.6 Stringhe di documentazione

Si stanno formando delle convenzioni sul contenuto e la formattazione delle stringhe di documentazione, ovvero le ``docstring''.

La prima riga dovrebbe essere sempre un sommario, breve e conciso, della finalità dell'oggetto. Per brevità, non vi dovrebbero comparire in forma esplicita il tipo o il nome dell'oggetto, dato che sono disponibili attraverso altri mezzi (eccetto nel caso in cui il nome sia un verbo che descriva un'azione della funzione). La riga dovrebbe iniziare con una lettera maiuscola e terminare con un punto.

Se la stringa di documentazione è composta da più righe, la seconda dovrebbe essere vuota, per separare visivamente il sommario dal resto della descrizione. Le righe successive dovrebbero costituire uno o più paragrafi che descrivono le convenzioni di chiamata dell'oggetto, i suoi effetti collaterali ecc.

L'analizzatore sintattico (parser) di Python non elimina l'indentazione da stringhe di testo di più righe, perciò i programmi che processano la documentazione devono toglierla da soli, se lo desiderano. Ciò viene fatto usando la seguente convenzione. La prima riga non vuota dopo la prima riga della stringa determina l'ammontare dell'indentazione presente nell'intera stringa di documentazione. Non si può usare la prima riga poiché di solito è adiacente alle virgolette di apertura della stringa, quindi la sua indentazione non è chiara nella stringa di testo. Gli spazi ``equivalenti'' a tale indentazione vengono quindi eliminati dall'inizio di tutte le righe della stringa. Non dovrebbero esserci righe indentate in misura minore, ma se ci fossero tutti gli spazi in testa alla riga dovrebbero essere eliminati. L'equivalenza in spazi dovrebbe essere testata dopo l'espansione dei tab (normalmente a 8 spazi).

Ecco un esempio di stringa di documentazione su più righe:

>>> def my_function():
...     """Non fa nulla, ma lo documenta.
...
...     Davvero, non fa proprio nulla.
...     """
...     pass
...
>>> print my_function.__doc__
Non fa nulla, ma lo documenta.

    Davvero, non fa proprio nulla.



Footnotes

... dell'oggetto).4.1
In realtà, chiamata per riferimento ad oggetto sarebbe una descrizione più appropriata, dato che se viene passato un oggetto mutabile, il chiamante vedrà qualsiasi modifica apportata nella chiamata (p.e. elementi inseriti in una lista).
Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.