In Python le funzioni sono oggetti di base ( "first-class objects") : l'istruzione def le crea ed assegna loro un nome, che e' un riferimento. Al solito si usano i due punti ed il rientro (indentation) per delimitare il corpo della funzione. Gli argomenti, fra parentesi tonde, seguono il nome della funzione; alla chiamata la funzione viene eseguita, producendo un oggetto, che e' il risultato della funzione e che viene restituito con un'istruzione "return" . In caso l'istruzione "return" non compaia nella funzione, viene restituito l'oggetto vuoto: "None".
La sintassi per la definizione di una funzione e' del tipo:
def nomefunzione(a,b,c): ''' docstring: descrizione funzione ''' d=a e=b+c return d+e
Per chiamare la funzione si utilizza una sintassi del tipo:
nomefunzione(r,s,t) g=nomefunzione(r,s,t) : qui il risultato della funzione e' assegnato a 'g'
All'inizio di una funzione puo' esserci una stringa multilinea che descrive la funzione Questa viene conservata nella variabile __doc__
def square(x): " " " Questa funzione eleva un numero al quadrato " " " return x*x
Le funzioni, come oggetti, hanno attributi, cui ci si puo' riferire con
nomefunzione.attributo
Alcuni attributi standard delle funzioni sono:
__doc__ : stringa descrittiva
__name__ : nome della funzione
dir(f) : mostra dizionario degli attributi della funzione f
Il nome della funzione e' semplicemente un riferimento alla funzione, si distingue dalla chiamata alla funzione, ove devono apparire le parentesi tonde dopo il nome. Il nome puo' stare in una lista, essere passato in argomento a funzioni, comparire nell'istruzione return, essere chiave di dizionari; solo le parentesi tonde dopo il nome indicano che la funzione va eseguita.
Per vedere se un nome e' un riferimento ad una funzione si puo' usare la funzione "callable(nome)", che restituisce True se l'oggetto cui ci si riferisce ha l'attributo "__call__", ovvero se e' una funzione.
Qui sotto vediamo, come esempio, una funzione che crea funzioni (function factory):
def maker(N): # funzione "factory" def action(X): # qui definisce una funzione return X ** N return action fa=maker(2) # creo una funzione fa che fa il quadrato fb=maker(3) # creo una funzione fa che fb il cubo fa(10) # produce 10*10 => 100 fb(10) # produce 10*10*10 => 1000
Gli argomenti sono passati per assegnazione: in Python le variabili sono riferimenti ad oggetti; nel passaggio degli argomenti, alla variabile nella funzione viene assegnato lo stesso oggetto della variabile corrispondente nella chiamata; cioe' viene fatta una copia dei riferimenti agli oggetti.
I tipi delle variabili vengono definiti solo all'assegnazione dei riferimenti, per cui una funzione, a priori, non sa quali sono i tipi degli argomenti ed eventuali inconsistenze producono errori solo quando la funzione viene eseguita. In questo modo Python implementa naturalmente il polimorfismo, cioe' una stessa funzione puo' essere usata per dati di tipo diverso. Ad esempio la funzione:
def somma(a,b): return a+b
se chiamata con: somma(3,2) produrra' 5, se chiamata con stringhe come argomenti: somma('aa','bb') restituira' la stringa 'aabb'. Se chiamata come: somma('a',1) produrra' un errore.
Riassegnare le variabili in argomento entro la funzione non ha effetti sul chiamante, ma gli oggetti mutabili possono essere cambiati nelle funzioni, operando sui loro riferimenti.
Le funzioni possono avere valori di default per gli argomenti. Ad esempio una funzione definita con:
def func(a='uno'):
puo' essere chiamata semplicemente con:
func()
ed il suo argomento a sara' il default: la stringa 'uno' ; oppure con
func('due')
ed il suo argomento a sara' la stringa 'due'
Una funzione puo' anche essere chiamata dando valori ai parametri per nome (keyword arguments), con una sintassi tipo:
func(a='sei')
in questo caso alla variabile "a" entro la funzione, viene assegnata la stringa "sei".
Una funzione puo' essere definita in modo che i suoi argomenti siano visti, entro la funzione, come una tupla o come un dizionario; le definizioni della funzione avranno in questi casi rispettivamente la sintassi:
def func(*nome):
def func(**nome):
Nel caso del dizionario gli argomenti sono passati per nome ed i nomi diventano le chiavi del dizionario.
Questi modo di passare gli argomenti possono essere combinati, in questo caso, nelle chiamate e nella funzione, vanno prima gli argomenti posizionali, poi quelli che finiscono in una tupla, ed infine, con passaggio per nome, quelli che finiscono nel dizionario:
def func(a,b,c): # esempio di funzione print(a) print(b) print(c) func(1,2,3) # chiamata con argomenti passati per posizione func( b=2,a=1,c=3) # argomenti passati per nome func(1,c=3,b=2) # argomenti passati per posizione, quello che resta per nome def func(*a): print(a) ''' Qui in a finisce una tupla di argomenti, la chiamata puo' avere numero variabile di argomenti func(1,2,3) stampa la tupla: (1,2,3) ''' def func(**d): print(d) ''' Qui gli argomenti finiscono in un dizionario gli argomenti sono passati per nome ed i nomi delle variabili sono le chiavi func(a=1,b=2,c=3) stampa: {'a': 1, 'c': 3, 'b': 2} ''' def func(a,*b,**d): print(a) print(b) print(d) ''' Vanno prima gli argomenti posizionali, poi quelli per la tupla, infine quelli per il dizionario. Chiamata come : func(1, 2,3,4, s=10,q=20 ) stampa: a=1 ; b=[2,3,4] ; d={s:10,q:20} '''
In Python3 abbiamo anche funzioni con argomenti passati per nome dopo quelli che finiscono in una lista:
def func(a,*b,c): Qui l'ultimo argomento che puo' essere data solo per nome" con chiamata: func(1,2, 3, c=40) , ed avremo a==1 ; b==[2,3] ; c==40
Anche le chiamate possono contenere liste o dizionari:
func(*a) : spacchetta l'iterabile a in modo implicito func(**d) : spacchetta il dizionario in: key1=val1, key2=val2 .. Se il dizionario e': {'key1':1,'key2':2,'key3':3} la chiamata equivale a: func(key1=1,key2=2,key3=3); e' come una chiamata per nome, con key1,key2,key3 che sono le variabili nella funzione.
La funzione restituisce un valore specificato nell'istruzione return. Se non viene eseguita l'istruzione return il valore restituito dalla funzione e' l'oggetto speciale di nome: "None", che e' per definizione un oggetto vuoto. Una funzione puo' restituire una tupla, con la sintassi tipo:
return a,b,c
Analoga a "return" e' la funzione "yeld", che ritorna un valore al chiamante; ma la volta successiva che la funzione viene chiamata l'esecuzione parte da dopo l'istruzione yeld. In questo modo si possono implementare iteratori. La sintassi e':
yeld a
Una funzione e' valida dal punto del programma in cui si incontra in poi; quando la funzione viene incontrata viene "eseguita", nel senso che il suo nome (che in realta' e' un riferimento) diviene valido ed ad esso sono associate le operazioni contenute nel corpo della funzione. In questo modo e' possibile definire una funzione in modo diverso a seconda del flusso del programma: ad esempio:
if a>b: def func(a,b): return a-b else: def func(a,b): return b-a
Queste istruzioni definiscono la funzione "func" come la differenza fra il piu' grande dei due valori in a e b. A seconda dei casi la funzione e' definita in modo diverso.
Una funzione puo' essere definita entro una funzione, ed allora e' vista solo li'.
Una variabile definita in una funzione non e' vista da fuori della funzione. Puo' avere stesso nome di una variabile esterna senza confusione.
Una variabile definita nel blocco in cui la funzione e' chiamata e' vista entro la funzione, ma non puo' essere modificata entro la funzione, a meno che non sia definita "global" entro la funzione.
Se, entro una funzione, una variabile e' definita come global sara' vista anche nel blocco in cui la funzione e' chiamata. Ma in ogni caso una variabile e' locale al file in cui si trova.
La dichiarazione di global e' del tipo:
global a,b,c
Questa regola di scope e' chiamata LEGB: Local, Enclosing, Global, Build-in ed e' il modo di cercare i nomi di Python
In Python3 una variabile dichiarata "nonlocal" in una funzione:
nonlocal a,b
e' definita nell'ambito del blocco superiore, deve gia' esistere nel blocco superiore, non e' vista oltre e non e' global. E' usata nelle funzioni definte entro funzioni.
Sono funzioni di una sola istruzione, senza nome, con sintassi:
lambda argomento1,argomento1,argomento3: espressione
Esempio:
f= lambda x,y : x+y f(2,3) produce 5
Le Lambda sono usate in contesti particolari, ove e' comodo mettere una piccola funzione in una sola riga, ad esempio per costruire una lista od un dizionario di funzioni:
L=[ (lambda x: x+x ) , ( lambda x: x*x) ] for f in L : print f(3) # Produce : 6 , 9
Esempio: dizionario di funzioni:
op={'somma':lambda *a: sum(a) , 'massimo':lambda *a: max(a)} op['somma'](10,20,30) # produce: 60 op['massimo'](10,20,30) # produce: 30
Sono funzioni che prendono in argomento una funzione, ci fanno modifiche, aggiunte, o fanno cose accessorie, poi restituiscono la funzione modificata:
def decname(funcname,a,b,c): ... operazioni varie con la funzione: funcname return funcname
C'e' una sintassi abbreviata quando si vuole applicare un decoratore ad una funzione:
@nomedecoratore def f() .....
Questo e' equivalente a definire la funzione f e poi applicargli il decoratore con:
f=nomedecoratore(f)
Esempio:
# creo un decoratore def decor1(func1): func1.a=33 func1.autore="io" return func1 #la uso per fare aggiunte a una funzione @decor1 def unafunc(c,d): r=c+d+unafunc.a print('somma: ',r,"author: ",unafunc.autore) #utilizzo la funzione: unafunc(1,2) somma: 36 author: io