LEGB rule — to zasada wyszukiwania przestrzeni nazw (scoping) w Pythonie. Skrót oznacza:
len, list itp.).Kiedy Python napotyka zmienną, najpierw szuka lokalnie, potem w zagnieżdżonych funkcjach, następnie w globalnej przestrzeni modułu, a na końcu w przestrzeni nazw obiektów wbudowanych:
def outer(): x = 'enclosing' def inner(): x = 'local' print(x) inner() outer() # 'local'
Jeśli wewnątrz inner() usuniemy przypisanie x, Python weźmie x z enclosing scope (outer). Jeśli nie ma zmiennej tam — szuka w globalnym, potem w wbudowanych.
Jak zmieni się działanie funkcji, jeśli zapomnisz zadeklarować
nonlocallubglobalprzy modyfikowaniu zmiennej zewnętrznego poziomu?
Odpowiedź:
Jeśli spróbujesz zmienić zmienną zewnętrznego scope-a wewnątrz zagnieżdżonej funkcji bez zadeklarowania nonlocal (lub global, jeśli zmienna jest w module), Python stworzy nową lokalną zmienną, nie zmieniając zewnętrznej.
Przykład:
x = 10 def foo(): x = 20 # To nowa lokalna x, globalna nie zmienia się foo() print(x) # 10
Aby zmienić globalną x:
def foo(): global x x = 20
Ze zmiennymi w zewnętrznej funkcji — użyj nonlocal.
Historia 1
W projekcie edukacyjnym używano zagnieżdżonych funkcji do liczenia liczby wywołań. Programista napisał:
def counter(): count = 0 def inc(): count += 1 return count return inc
Ale wystąpił UnboundLocalError, ponieważ count wewnątrz inc() był traktowany jako nowy (tworzony nowy lokalny count). Rozwiązanie: zadeklarować count jako nonlocal.
Historia 2
W aplikacji webowej chciano w obsłudze modyfikować globalnie zdefiniowany cache:
cache = {} def add_to_cache(key, value): cache[key] = value
Jednak jeśli wewnątrz funkcji spróbowano by całkowicie przepisać cache, na przykład cache = {key: value}, stworzyłoby to nowy lokalny cache, niepowiązany z globalnym, z powodu braku global cache.
Historia 3
W dużym systemie ETL napotkano błąd: programiści pozostawili zmienną result bez jawnej inicjalizacji wewnątrz funkcji, spodziewając się, że weźmie ją z zewnętrznego scope. Ale po kilku iteracjach wynik zaczął się psuć, ponieważ wewnątrz była instrukcja result += value, a nie result = result + value (bez nonlocal/global) — i result była inicjalizowana na nowo przy każdym wywołaniu.