11. Le Funzioni in Python

Introdurremo oggi un argomento più avanzato rispetto a quanto trattato finora, ma assolutamente fondamentale.

Parleremo di funzioni: vedendo cosa sono, a cosa servono e come si usano in Python!


Riutilizzo del codice

Uno dei concetti comuni nel mondo della programmazione è il concetto di riutilizzo del codice. Generalmente, più un programma diventa complesso e strutturato, più saranno le righe di codice che lo compongono. Affinché sia possibile tenere questo codice quanto più leggero possibile, uniforme e semplice da mantenere, è molto importante evitare ripetizioni superflue.

Finora abbiamo visto alcuni esempi di riutilizzo del codice: abbiamo imparato a usare i cicli di controllo, i moduli della Standard Library e alcune funzioni integrate come la funzione print(), usata più volte e in parti diverse dei nostri programmi.

In aggiunta alle funzioni integrate offerte da Python, potete scrivere le vostre funzioni per fargli fare ciò che desiderate: in questa lezione imparerete a fare proprio questo.

Ciò risulta molto importante perché ci permetterà come vedremo, di strutturare i nostri programmi in maniera efficiente ed elegante.


Le funzioni in Python

L'idea dietro al concetto di funzione è quella di assegnare una porzione di codice e delle variabili chiamate parametri ad un nome, e mediante questo nome che funge da collegamento richiamare tutto il codice associato.

Per certi versi il concetto è simile a quello di variabile che abbiamo visto nella lezione numero 3, solo che in questo caso possiamo richiamare non solo dei dati ma anche delle istruzioni.

E così come possiamo creare dei semplici programmi salvandoli all'interno di file con estensione .py, per eseguirli tramite terminale, possiamo usare le funzioni per creare dei mini programmi ed eseguirli dall'interno del codice stesso.

Le funzioni sono quindi fantastiche per fare in modo di ridurre il numero di volte che un pezzo di codice deve essere scritto in un programma e per mantenere il nostro software ordinato.


La parola chiave def

Per definire una funzione utilizziamo la parola chiave def seguita dal nome che vogliamo dare a questa sezione del nostro programma, quindi a questa funzione. Come nel caso delle variabili, cercate di usare dei nomi che siano rappresentativi dello scopo della funzione.

Il nome viene seguito da una coppia di parentesi, e all'interno delle parentesi metteremo i parametri, ovvero delle variabili necessarie al funzionamento della funzione. Non tutte le funzioni avranno bisogno di parametri: in questo primo esempio lasceremo le parentesi vuote.

def say_my_name():
    name = input("Come ti chiami? ")
    print("Il tuo nome è: ", name)

Seguendo la logica di Python, il codice inserito all'interno della funzione deve essere indentato. All'interno della funzione potete aggiungere qualsiasi istruzione riteniate opportuna.

Per eseguire la funzione dobbiamo richiamarla, e per fare una chiamata alla funzione possiamo semplicemente utilizzare il suo nome seguito dalle parentesi

# definizione della funzione
def say_my_name():
    name = input("Come ti chiami? ")
    print("Il tuo nome è: ", str(name))
    
# chiamata della funzione
say_my_name()

Facciamo ora un secondo esempio in cui utilizzeremo i parametri che abbiamo prima accennato. Definiamo una funzione addizione a cui passeremo due numeri e che ci restituirà la somma tra i due:

def addizione(a, b):
    print("Questa è la funzione addizione.")
    print("Fornisce la somma di due numeri passati come parametri.")
    risultato = a + b
    print("Il risultato dell'addizione è " + str(risultato))

Eseguiamo la funzione mediante la chiamata al suo nome e passiamoli due numeri come argomento tra parentesi.

addizione (15, 5)

L'argomento è il nome che viene dato al valore passato per essere depositato nella variabile parametro. Quindi i parametri in questo caso sono a e b, e gli argomenti dei parametri 15 e 5.

# output

Questa è la funzione addizione.
Fornisce la somma di due numeri passati come parametri.
Il risultato dell'addizione è 20

Non ci sono particolari limiti per quanto riguarda i parametri che è possibile passare, ma badate di sceglierli sempre con cura e di dargli anche in questo caso dei nomi specifici.


Le docstring di Python

Una docstring è una stringa di testo che viene inserita all'inizio di una funzione, di una classe o di un modulo e che serve a documentare il codice. A differenza dei commenti, le docstring sono considerate parte del codice e vengono utilizzate per generare la documentazione automaticamente: questo significa che possono essere lette da strumenti di documentazione come Sphinx e possono essere utilizzate per generare documentazione HTML e PDF. Le docstring si scrivono inserendo il testo all’interno di stringhe multiriga definite con tripli apici “”” “””:

def somma(a, b):
    """
    Questa funzione prende due argomenti, a e b, e restituisce la loro somma.
    """
    return a + b

In questo caso, la docstring descrive lo scopo della funzione e il valore che viene restituito. Possono essere utilizzate anche per documentare i parametri di una funzione, ad esempio:

def saluta(nome):
    """
    Questa funzione prende un argomento, nome, e restituisce una stringa di saluto.

    :param nome: il nome della persona da salutare
    :return: una stringa di saluto
    """
    return "Ciao, " + nome + "!"

In questo caso, la docstring descrive il parametro nome e il valore di ritorno della funzione.

Quando si utilizza una funzione che ha una docstring in Visual Studio Code, è possibile visualizzarla attraverso la funzionalità IntelliSense dell'editor: questo significa che quando si digita il nome della funzione, verrà mostrato un suggerimento che include la docstring della funzione, compresi i parametri e il valore che viene restituito. Vediamo un esempio:

def calcola_media(valori):
    """
    Calcola la media dei valori nella lista fornita.
    
    Args:
        valori (list): una lista di numeri
    
    Returns:
        float: la media dei numeri nella lista
    """
    somma = 0
    for valore in valori:
        somma += valore
    media = somma / len(valori)
    return media

Se scriviamo il nome della funzione e ci andiamo sopra con il mouse, vedremo la docstring:

VSCode Calcola Media


Parametri opzionali e valori di default

Talvolta ci troveremo in una situazione in cui è necessario avere dei parametri opzionali per le nostre funzioni, ovvero fare in modo che queste funzionino con o senza l'aggiunta di valori in questi specifici parametri. Per rendere questo possibile, possiamo associare a questi parametri un valore di default.

Immaginate che stiate andando a comprare un laptop nuovo. Due caratteristiche da tenere sott'occhio sono sicuramente RAM e modello della CPU, che quindi sceglieremo. Come vi sarà capitato, vi viene chiesto se volete acquistare anche un pacchetto antivirus, e voi potete scegliere se lo volete oppure meno.

Proviamo a portare questo esempio in una funzione:

def laptop_nuovo(ram, cpu, antivirus=False):
    print("Il nuovo laptop avrà le seguenti caratteristiche:")
    print("ram: " + ram)
    print("cpu: " + cpu)
    if antivirus == True: # comparazione esplicita per fini didattici
        print("Hai comprato anche un antivirus!")

Quindi qualora non ci interessi acquistare l'antivirus possiamo semplicemente ignorare questo parametro a cui è stato dato il valore di default False:

>>> laptop_nuovo("16gb", "i7")

Il nuovo laptop avrà le seguenti caratteristiche:
ram: 16gb
cpu: i7

Diversamente se vogliamo l'antivirus ci basta passare True come valore per il parametro antivirus:

>>> laptop_nuovo("16gb", "i7", antivirus=True)

Il nuovo laptop avrà le seguenti caratteristiche:
ram: 16gb
cpu: i7
Hai comprato anche un antivirus!


L'istruzione return in Python

Prestate ora attenzione a un dettaglio.

Nella nostra funzione addizione, per fare in modo di mandare in output il risultato della somma abbiamo utilizzato una seconda funzione, ovvero la funzione print(), a cui abbiamo passato il risultato dell'operazione.

Proviamo a togliere tutti i print() e lasciare solo la variabile risultato e richiamiamo quindi la funzione:

def addizione(a, b):
    risultato = a + b

addizione(3, 3)

# output

Come immaginabile, la funzione non ci mostra nulla! Per fare in modo che la nostra funzione restituisca il risultato in maniera autonoma dobbiamo utilizzare il comando return.

Potremo quindi usare questo dato passandolo ad esempio a una variabile o usando la funzione print():

def addizione(a, b):
    risultato = a + b
    return risultato


risultato = addizione(3, 6)
print(risultato)

# output 
9

...ed ecco che ora otteniamo nuovamente il contenuto di risultato!


Il tipo nullo di Python: NoneType

Se non definiamo un valore da restituire tramite return, la funzione restituirà il tipo di dato nullo o null di Python, il None Type. Modifichiamo il codice scritto in precedenza e facciamo una prova

def addizione(a, b):
    risultato = a + b


risultato = addizione(3, 6)
print(risultato)
print(type(risultato))

# output 
None
<class 'NoneType'>