LEGB

Criterio con il quale l’interprete cerca una variabile in un determinato namespace. Local Enclosing Global Built-in

Il lookup di un riferimento avviene prima nel namespace locale (all’interno della funzione corrente), se non c’è si va a cercare nel namespace della funzione che ha chiamato la funzione corrente, ovvero il namespace enclosing. Se ancora non si è trovato nulla, si va a cercare nel namespace globale, ed infine nel namespace built-in.

Bypass di LEGB

Si può in parte bypassare questo comportamento utilizzando le keywords nonlocal e global.

def A():
    global x               # Si riferisce alla variabile definita a riga 12
    def B():
        global x           # Si riferisce alla variabile definita a riga 12
        nonlocal y         # Si riferisce alla variabile definita a riga 8
        x = 2              # Senza la definizione di riga 4 creerebbe una nuova variabile
        y = 3              # Senza la definizione di riga 5 creerebbe una nuova variabile
    y = 2
    print("Riga 9:",x,y)
    B()
    print("Riga 11:",x,y)
x = 1					   # riga 12
y = 1
print("Riga 14:",x,y)
A()
print("Riga 16:",x,y)

Esempi di stranezze

class foo:
    s = 0 # Assegnamento necessario perche l'attributo sia "definito"
    def get():
        return s
    def somma(x,y):
        s = x+y
		
f = foo()

Se si prova a leggere l’attributo s da fuori, non c’è nessun problema f.s restituisce 0.

Quando si prova a chiamare il metodo f.foo(), però, si riceve un errore (get() takes 0 positional arguments but 1 was given). L’attributo s non sembra essere visibile.

Si prova a modificare la funzione get() in questo modo, basandosi sul messaggio di errore:

def get(self):
	return s

Anche in questo caso, chiamando f.get() si riceve un errore (name 's' is not defined). Ancora non si riesce a leggere l’attributo.

s non viene trovato perché python lo cerca utilizzando LEGB, ovvero dovunque tranne che a livello di oggetto. Se l’attributo non è quindi qualificato, non viene trovato. Se l’attributo viene qualificato (oggetto.attributo), allora la ricerca avviene in due parti:

  1. oggetto viene cercato tramite LEGB
  2. una volta trovato oggetto, si inizia a cercare l’attributo in esso, ovviamente utilizzando la catena ereditaria via MRO

Ricerca l’attributo in oggetto. Se è una classe cerca direttamente nelle superclassi di oggetto. Se non è una classe posto T = type(oggetto), cerca in T e poi casomai nelle superclassi sempre ordinate tramite MRO.

Si potrebbe sostituire s con return foo.s, utilizzando l’attributo “statico” della classe, ma, appunto, utilizza lo stesso dato in tutte le istanze, cosa rischiosa.