programowanieProgramista Python

Co to jest 'LEGB rule' w Pythonie? Jak działa podczas wyszukiwania nazwy zmiennej? Podaj przykłady sytuacji, w których błędne zrozumienie tej zasady prowadzi do niepozornych błędów.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

LEGB rule — to zasada wyszukiwania przestrzeni nazw (scoping) w Pythonie. Skrót oznacza:

  • Local — lokalna przestrzeń nazw (wewnątrz funkcji);
  • Enclosing — przestrzenie nazw wszystkich funkcji zagnieżdżonych;
  • Global — przestrzeń modułów (zmienne globalne);
  • Builtin — wbudowane nazwy Pythona (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.


Pytanie z haczykiem.

Jak zmieni się działanie funkcji, jeśli zapomnisz zadeklarować nonlocal lub global przy 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.


Przykłady rzeczywistych błędów wynikających z nieznajomości subtelności tematu.


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.