Vai al contenuto

Funzioni

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 integrate (ad esempio, le funzioni print() e input()) che la possibilità di creare funzioni personalizzate; in questo capitolo verranno analizzate le seconde, delle prime ci occuperemo in una successiva sezione.

Definire una funzione

Si può definire una funzione adoperando la seguente, generica, sintassi:
def nome_funzione(parametri):
    """docstring"""
    blocco function
In particolare, la definizione di una funzione ha le seguenti componenti:
  • la parola chiave def, che contrassegna l'inizio dell'intestazione della funzione;
  • un nome di funzione che la identifichi in modo univoco;
  • parametri o argomenti (opzionali) attraverso i quali passiamo valori alla funzione;
  • due punti : per contrassegnare la fine dell'intestazione della funzione;
  • stringa di documentazione facoltativa, detta docstring, per descrivere cosa fa la funzione;
  • un blocco di codice che contiene le istruzioni che saranno eseguite dalla funzione;
  • un'istruzione di return (opzionale e che vedremo in seguito) che consente alla funzione di restituire un valore.
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(eventuali_parametri)

Docstring

La prima stringa dopo l'intestazione della funzione è chiamata docstring ed è l'abbreviazione di documentation string. Tale stringa è utilizzata per spiegare, in breve, quello che fa la funzione.
  Sebbene facoltativa, l'inserimento di una docstring è una buona pratica di programmazione, che consente allo sviluppatore e agli utilizzatori del programma di comprendere meglio la logica della funzione. Si consideri, a titolo di esempio, il seguente codice:
def lavori_in_corso():
    """
    In questa funzione implementeremo una delle
    richieste del nostro cliente, per il momento 
    non ancora nota.
    """
    pass

print(lavori_in_corso.__doc__)
Ebbene, in questo esempio stiamo supponendo che il nostro cliente voglia sviluppare una certa funzione, non ancora nota, che codificheremo in seguito (da qui l'utilizzo della già nota keyword pass). Eseguendo lo script, otterremo il seguente messaggio, contenente la descrizione della funzione:
In questa funzione implementeremo una delle
richieste del nostro cliente, per il momento 
non ancora nota.

Arguments

Come preannunciato, possiamo passare dei parametri di input alle funzioni. Questi ultimi svolgono sostanzialmente la funzione di placeholders (segnalibri), comunicando all'applicazione il numero di argomenti che dovranno essere passati alla funzione; questi ultimi potranno essere valorizzati arbitrariamente dallo sviluppatore sulla base delle proprie esigenze. A questo proposito, la funzione presentata nell'esempio seguente ha il compito di concatenare tre parametri (nome, cognome e msg) restituendoli in output all'interno di un stringa:

def saluto(nome, cognome, msg):
    """
    La funzione genera stampa sul prompt 
    una stringa prodotta dalla concatenazione 
    dei valori dei parametri passati ad essa.
    """
    print("Salve, " + nome + " " + cognome + ". " + msg)

# chiamata alla funzione
saluto("Franco", "Rossi", "Come stai?")
Il risultato dell'esecuzione del codice proposto e della chiamata alla funzione saluto sarà il seguente:
Salve, Franco Rossi. Come stai?

Default arguments

Python consente anche la definizione di parametri di default attraverso l'operatore di assegnamento =. Qui un esempio in cui al parametro msg viene assegnato il valore di default "Come stai?":

def saluto(nome, cognome, msg = "Come stai?"):
    """
    La funzione genera stampa sul prompt 
    una stringa prodotta dalla concatenazione 
    dei valori dei parametri passati ad essa.
    """
    print("Salve, " + nome + " " + cognome + ". " + msg)

# chiamata alla funzione
saluto("Franco", "Rossi")
saluto("Andrea", "Bianchi", "Erano settimane che non ci incontravamo.")
Il relativo output sarà:
Salve, Franco Rossi. Come stai?
Salve, Andrea Bianchi. Erano settimane che non ci incontravamo.
Si noti che, quando nella chiamata alla funzione non viene specificato il parametro msg, allora quest'ultimo assume il suo valore di default.
  A livello di sintassi, il linguaggio Python non consente di definire i parametri di default prima di quelli non di default. Infatti, l'intestazione:
def saluto(msg = "Come stai?", nome, cognome):
produrrà il seguente errore:
SyntaxError: non-default argument follows default argument

Keyword arguments

Quando chiamiamo una funzione con alcuni valori, questi valori vengono assegnati agli argomenti in base alla loro posizione.

Ad esempio, riferendoci alla funzione saluto(), durante la chiamata saluto("Andrea", "Bianchi", "Come stai?"), il valore Andrea viene assegnato al parametro nome, Bianchi al parametro cognome, e infine Come stai? al parametro msg.

  Python consente di chiamare le funzioni usando anche le keyword dei parametri. Quando chiamiamo le funzioni in questo modo, l'ordine dei parametri può essere modificato. Le chiamate successive sono infatti tutte valide, e producono lo stesso risultato.

saluto(nome = "Andrea", cognome = "Bianchi", msg = "Come stai?")
saluto(cognome = "Bianchi", nome = "Andrea", msg = "Come stai?")
saluto(msg = "Come stai?", cognome = "Bianchi", nome = "Andrea")
Occorre tuttavia evitare di mixare i due metodi, cioè assegnare alcuni valori tramite posizione, e altri tramite keyword. Infatti, la chiamata:
saluto(nome = "Andrea", "Bianchi", "Come stai?")
produrrà l'errore:
SyntaxError: non-keyword arg after keyword arg

Arbitrary arguments

A volte, non conosciamo in anticipo il numero di argomenti che saranno passati in una funzione. Tuttavia, Python ci permette di gestire questo tipo di situazione attraverso chiamate di funzioni con un numero arbitrario di argomenti. In tal caso, nella definizione della funzione, basterà utilizzare l'asterisco * prima del nome per indicare un parametro composto da un numero indefinito di argomenti. Ecco un esempio.
def ciao(*args):
   """
   Questa funzione saluta tutte le persone contenute 
   nella tupla args.
   """

   for arg in args:
       print("Ciao ", arg)

ciao("Monica","Grazia","Luigi","Giovanni")
Il relativo output sarà:
Ciao Monica
Ciao Grazia
Ciao Luigi
Ciao Giovanni
Analogamente, può capitare di dover passare alla funzione un numero indefinito di keyword arguments. In tal caso, si potrà ricorrere al doppio asterisco ** prima del nome per indicare un parametro composto da un numero indefinito di keyword arguments. Qui un esempio:
def portate(**kwargs):
    """
    Questa funzione elenca le portate
    previste nel menu.
    """

    for key in kwargs:
        value = kwargs[key]
        print(key + ": " + value)

portate(antipasto = "mozzarelle", primo = "carbonara", secondo = "bistecca", dolce = "budino")
L'output prodotto sarà:
antipasto: mozzarelle
primo: carbonara
secondo: bistecca
dolce: budino

Return

Esistono anche funzioni che restituiscono dei valori. In tal caso, il valore restituito seguirà l'istruzione return, la quale chiude di fatto il blocco della funzione stessa. Qualora l'istruzione return sia assente, il valore restituito sarà invece uguale a None. Qui di seguito proponiamo una funzione che restituisce il valore assoluto di un numero:

def valore_assoluto(num):
    """
    Questa funzione restituisce il valore assoluto
    del numero inserito in ingresso.
    """

    if num >= 0:
        return num
    else:
        return -num

# Output: 2
print(valore_assoluto(2))

# Output: 4
print(valore_assoluto(-4))

Scope e ciclo di vita

Lo scope (o visibilità) di una variabile è la parte di un programma in cui la variabile è riconosciuta. I parametri e le variabili definiti all'interno di una funzione non sono visibili dall'esterno. Quindi, hanno uno scope locale.
  Il ciclo di vita di una variabile è invece il periodo attraverso il quale la variabile viene inserita e poi cancellata dalla memoria. Il ciclo di vita delle variabili all'interno di una funzione è pari al tempo necessario all'esecuzione della funzione stessa. Tali variabili sono distrutte una volta che la funzione termina, ragion per cui una funzione non ricorda i valori assunti dalle sue variabili locali nelle chiamate precedenti. Ecco un esempio per illustrare lo scope e il ciclo di vita di una variabile all'interno di una funzione.
def my_func():
    x = 10
    print("Il valore di x nella funzione è: ", x)

x = 20
my_func()
print("Il valore di x fuori dalla funzione è: ", x)
L'output del programma sarà:
Il valore di x nella funzione è: 10
Il valore di x fuori dalla funzione è: 20
Possiamo vedere che il valore di x è inizialmente 20. Anche se la funzione my_func() ha cambiato il valore di x a 10, non ha influenzato il suo valore al di fuori della funzione. Questo perché la variabile x all'interno della funzione è diversa da quella all'esterno. Sebbene abbiano lo stesso nome, sono due variabili differenti con scope differenti. D'altra parte, le variabili esterne alla funzione sono visibili dall'interno, vale a dire che hanno uno scope globale. Possiamo leggere questi valori dall'interno della funzione ma, badate bene, non possiamo modificarli. Affinché una funzione sia in grado di modificare il valore delle variabili al di fuori di essa, tali variabili devono essere dichiarate come globali utilizzando la parola chiave global. Infatti, eseguendo lo script:
def my_func():
    global x
    x = 10
    print("Il valore di x nella funzione è: ", x)

x = 20
my_func()
print("Il valore di x fuori dalla funzione è: ", x)
otterremo l'ouput:
Il valore di x nella funzione è: 10
Il valore di x fuori dalla funzione è: 10