12. Variabili Globali e Variabili Locali

In questa lezione approfondiremo il discorso su variabili e funzioni parlando di variabili globali e variabili locali.

Finora abbiamo detto, semplificando il tutto a fini didattici, che possiamo immaginare le variabili come delle scatole in cui depositiamo dei valori. Possiamo quindi richiamare e modificare questo valore utilizzando il nome della variabile.

x = 3
x += 1
x
4

Abbiamo inoltre parlato di funzioni, e abbiamo visto che anche qui possiamo e siamo incoraggiati ad utilizzare le variabili: si tratta di uno degli elementi indispensabili di Python e della programmazione in generale. Ma fino a dove ci possiamo spingere nell'utilizzo delle variabili? Fino a che punto è davvero possibile utilizzarle e modificarle nei nostri programmi?

Per rispondere a queste domande andremo ora ad approfondire il tema parlando di Variabili Globali e Variabili Locali. Partiamo da un esempio:

x = 15

def funzione_esempio():
    return x

print(funzione_esempio())
15

La nostra funzione esempio ci restituisce il valore assegnato alla variabile x.

Proviamo però ora a modificare leggermente la nostra funzione; diciamo che vogliamo aggiungere 2 al 15 presente in x.

x = 15

def funzione_esempio():
    x += 2
    return x

print(funzione_esempio())

# output in Python 3.11
UnboundLocalError: cannot access local variable 'x' where it is not associated with a value


# in versioni precedenti di Python, il messaggio d'errore mostrato era
UnboundLocalError: local variable 'x' referenced before assignment

Come mai abbiamo ottenuto questo errore? Python ci informa del fatto che ci stiamo riferendo a una variabile prima di averla dichiarata e assegnata, eppure nell'esempio precedente tutto ha funzionato a meraviglia e la variabile x è proprio li!


Local Scope e Global Scope

La chiave di lettura di questo comportamento apparentemente bizzarro è la seguente, prestate attenzione: in Python il codice e quindi anche le variabili, possono essere "salvati" in due ambienti diversi chiamati local scope e global scope, traducibili sostanzialmente come ambito di visibilità locale e globale

Potete pensare a questi due ambienti come a dei contenitori distinti in cui vengono definite e assegnate le variabili, un contenitore globale e un contenitore locale. Quando uno di questi contenitori viene distrutto, quindi quando uno di questi ambiti viene distrutto, lo stesso accade per i valori delle variabili in esso salvate che vengono quindi dimenticati.


Variabili Locali e Variabili Globali

Un ambito locale viene creato ogni volta che una funzione viene chiamata, e distrutto dopo che la funzione restituisce un valore con return.

È come se ogni volta che una funzione viene processata, Python le fornisse un contenitore e le dicesse: bene, metti le tue variabili e il tuo codice quà dentro. Possono quindi esistere tanti Local Scope quante funzioni abbiamo in esecuzione. Le variabili dichiarate all'interno di qualsiasi funzione, quindi dell'Ambito Locale della funzione, sono chiamate variabili locali.

Dall'altro lato invece, esiste e può esistere un unico Ambiente Globale, che viene creato automaticamente da Python all'avvio del programma e distrutto alla chiusura del programma. È l'ambito principale e tutte le variabili che vengono dichiarate qui, quindi all'esterno di una funzione, sono chiamate proprio variabili globali, e restano pertanto in vita dall'avvio del programma "principale" fino alla sua chiusura.

È possibile accedere alle variabili globali da qualsiasi parte del programma, mentre è possibile accedere alle variabili locali solamente dall'ambito locale della funzione in cui sono contenute.

Nel nostro esempio, x è proprio una variabile globale, e otteniamo un errore perché seppur è vero che da un local scope si puó accedere alle variabili del global scope, il loro utilizzo all'interno della funzione è limitato.


L'istruzione global

Per poter modificare il valore di una variabile globale dall'interno di una funzione, come abbiamo provato a fare con la nostra x, dobbiamo prima dichiarare alla funzione le nostre intenzioni mediante l'istruzione global. Modifichiamo l'esempio precedente:

x = 15

def funzione_esempio():
    global x
    x += 2
    return x


print(funzione_esempio())
17

Ed ecco che ora ci è possibile modificare il valore della variabile x, perché Python sa che ci stiamo riferendo alla x presente nel global scope!

Un altro modo per poter utilizzare il valore di una variabile globale in maniera più complicata all'interno di una funzione è creando una variabile locale a cui assegnamo il valore della variabile globale. Potremo quindi poi modificare la nostra nuova variabile locale, aggirando così la limitazione.

x = 15

def funzione_esempio():
    y = x
    y += 2
    return y

print(funzione_esempio())
17

Come accennato inoltre, al contrario delle globali, è possibile accedere alle variabili locali solamente dall'ambito locale della funzione in cui sono contenute, quindi non è possibile accedervi dal global scope o dal local scope di un'altra funzione e otterremmo un errore qualora provassimo a forzare questa regola. Ad esempio:

def mia_funzione():
    val = 24
    print(val)

new_val = val + 6

# output
NameError: name 'val' is not defined

Potete utilizzare le variabili di una funzione solo se queste ci vengono passate dal return della funzione. Ad esempio, facciamo una piccola modifica:

def mia_funzione():
    val = 24
    return val

new_val = mia_funzione() + 6
print(new_val)

# output
30

Ed ecco che ora possiamo effettuare una somma con spam dall'ambito principale del programma.


Local e Global Scope: quanto sono importanti?

È bene imparare la distinzione tra local e global scope perché si tratta di concetti estremamente importanti. L'esistenza degli ambiti è utilissima ad esempio per ridurre il numero di bug e aumentare la robustezza dei programmi.

Pensate a che caos se in un programma complesso tutte le variabili fossero globali, e per colpa di un errore in una della tante funzioni del programma i valori delle variabili fossero perennemente sballati, causando un crash sistematico. Inoltre restando in memoria fino alla fine del programma, in programmi complessi le variabili globali sono sinonimo di pesantezza e spreco di risorse.

L'utilizzo di Variabili Locali è quindi estremamente incoraggiato, e quello di Variabili Globali decisamente SCONSIGLIATO!