E’ possibile definire le funzioni come degli insiemi composti da una o più istruzioni necessarie per lo svolgimento di un determinato compito; uno dei vantaggi derivanti dall’utilizzo di questi costrutti riguarda il fatto che essi possono essere definiti una volta sola e poi utilizzati in più di un’occasione all’interno della medesima applicazione, ciò avverrà tramite un meccanismo denominato "chiamata alla funzione". In questo modo si eviteranno inutili ripetizioni di codice con un evidente risparmio di tempo in sede di sviluppo a cui si accompagna la possibilità di creare sorgenti più snelli, leggibili e performanti. Python prevede sia funzioni native del linguaggio che la possibilità di creare funzioni personalizzate, in questo capitolo verranno analizzate le seconde, delle prime ci occuperemo nel corso del resto della trattazione.
A livello sintattico una funzione prevede un’intestazione costituita dalla parola chiave def seguita dal nome della funzione (che potrà essere stabilito arbitrariamente dallo sviluppatore ma dovrà essere univoco nel contesto dell’applicazione corrente) e da delle parentesi tonde che potranno contenere o meno dei parametri sui quali la funzione dovrà agire; i parametri potranno esse uno o più di uno, nel caso di più parametri questi ultimi andranno separati con un virgola:
# Intestazione di una funzione in Python
def nome_funzione(eventuale_parametro, ..):
Dopo l’intestazione, la cui fine verrà marcata tramite il simbolo dei due punti (":") è possibile documentare la funzione definita tramite un particolare tipo di commento (opzionale) denominato docstring, esso dovrà essere delimitato da tre apici all’inizio e alla fine del testo, rispetterà la stessa indentazione delle istruzioni in cui è inserito e non parteciperà all’esecuzione della funzione, motivo per il quale sarà visibile soltanto a livello di sorgente:
"""Il funzionamento di questa funzione è stato documentato
tramite docstring"""
Infine, una funzione può determinare o meno la restituzione di un valore di ritorno introdotto tramite la keyword return. Per utilizzare una funzione è richiesta una chiamata esplicita ad essa in un qualsiasi punto del codice, tale operazione potrà essere effettuata invocando il nome della funzione e passando ad essa i parametri eventualmente richiesti sotto forma di valori o di variabili:
nome_funzione(eventuale_parametro, ..)
Funzioni prive di parametri
Il caso più semplice di una funzione definita dall’utente in Python è quello nel quale non sono previsti né parametri da passare ad essa né un valore di ritorno; l’esempio seguente mostra la definizione di una funzione il cui unico compito è quello di stampare una stringa.
# Esempio di funzione priva di argomenti
# definizione della funzione
def stampa_stringa():
# documentazione della funzione
"""La funzione stampa una stringa in output"""
# istruzione della funzione
print("Blah! Blah! Blah!")
# chiamata alla funzione
stampa_stringa()
Da notare che le parentesi tonde che seguono il nome della funzione dovranno essere sempre presenti, sia in sede di definizione che di chiamata, anche nel caso in cui non dovessero essere necessari dei parametri.
Passaggio di parametri alle funzioni
Riguardo a questi ultimi, essi svolgono sostanzialmente la funzione di placeholders ("segnalibri") , comunicando all’applicazione il numero di argomenti che dovranno essere passati alla funzione definita dall’utente, questi ultimi potranno essere valorizzati arbitrariamente dallo sviluppatore sulla base delle proprie esigenze e della tipologia di progetto implementato. A questo proposito, la funzione presentata nell’esempio seguente ha il compito di concatenare due parametri ("nome" e "cognome") restituendoli in output all’interno di un stringa:
# Esempio di funzione con passaggio di parametri in Python
# definizione della funzione
# e dei parametri da elaborare
def saluto(nome, cognome):
# documentazione della funzione
"""La funzione genera in output
una stringa prodotta dalla concatenazione
dei valori dei parametri passati ad essa"""
# valore di ritorno
print("Ti chiami " + nome + " " + cognome + ". Ciao!")
# chiamata alla funzione
saluto("Homer", "Simpson")
Il risultato dell’esecuzione del codice proposto e della chiamata alla funzione "saluto" sarà quindi il seguente:
Ti chiami Homer Simpson. Ciao!
Funzioni con valore di ritorno
Diverso il caso delle funzioni che prevedono un valore di ritorno, esso in pratica consente di uscire dal flusso di esecuzione della funzione in seguito alla chiamata dopo la restituzione di un dato specifico per la cui generazione è stata definita la funzione stessa. L’esempio seguente mostra una funzione che accetta due parametri ("moltiplicando" e "moltiplicatore"), il valore di ritorno previsto non sarà altro che il risultato della moltiplicazione tra questi due argomenti che fungeranno da fattori:
# Esempio di funzione con valore di ritorno
# definizione della funzione
# e dei parametri da elaborare
def prodotto(moltiplicando, moltiplicatore):
# documentazione della funzione
"""La funzione restituisce
il risultato di una moltiplicazione
sulla base dei parametri passati ad essa"""
# valore di ritorno
return moltiplicando * moltiplicatore
# chiamata alla funzione
print ("Il prodotto della moltiplicazione è " + str(prodotto(90, 2)))
Nel caso specifico, in fase di chiamata sono stati passati alla funzione i fattori "90" e "2" ottenendo come risultato il seguente output (ma sarebbe stato possibile utilizzare qualsiasi altra coppia di valori numerici):
Il prodotto della moltiplicazione è 180
Utilizzare pass per annotare una funzione da implementare
La parola chiave pass è una sorta di commento che potrà essere utilizzato per comunicare all’esecutore di Python la mancata implementazione di un costrutto da completare in un momento successivo, come nell’esempio seguente:
def nome_funzione(eventuale_parametro, ..):
pass
A differenza degli altri commenti pass non viene ignorato dall’engine del linguaggio e, anche se non è destinato a produrre alcun output, partecipa al flusso di esecuzione dell’applicazione senza dar luogo ad alcuna operazione e segnalando che la porzione di codice precedente non deve essere presa in considerazione perché incompleta. Molto utile nel caso di funzioni il cui sorgente deve essere ancora perfezionato, pass è comunque adottabile anche in altri contesti come per esempio quello di un ciclo:
for val in fibonacci:
pass