Объясни LEGB-правило поиска имён.
LEGB — порядок поиска имени: Local → Enclosing → Global → Builtins. Область определяется на этапе компиляции: любое присваивание в функции делает имя локальным на всю функцию, а для записи во внешние области нужны global и nonlocal.
Что такое LEGB
LEGB — порядок, в котором интерпретатор Python ищет значение имени при его чтении: Local (тело текущей функции), Enclosing (объемлющие функции, для замыканий), Global (модуль) и Builtins (модуль builtins: len, print, list и т.д.). Первое совпадение и определяет, чему равно имя.
Как Python определяет область
Область вычисляется во время компиляции функции, а не в рантайме. Правило простое: если в теле функции имя где-либо присваивается (через =, for x in ..., except ... as x, with ... as x, аргумент функции), оно считается локальным на всю функцию. Поэтому чтение такой переменной до присваивания даёт UnboundLocalError, а не падение в Global.
Чтобы писать во внешние области используют global (модульный уровень) и nonlocal (ближайшая объемлющая функция, не модуль).
Пример всех четырёх уровней
x = "global" # G
def outer():
x = "enclosing" # E
def inner():
x = "local" # L
return x
return inner(), x
print(outer()) # ('local', 'enclosing')
print(x) # global
print(len([1, 2])) # B — len берётся из builtins
Демонстрация UnboundLocalError и nonlocal
counter = 0
def bad():
# counter присваивается ниже => он локальный на всю функцию
print(counter) # UnboundLocalError
counter = 1
def make_counter():
n = 0
def inc():
nonlocal n # без nonlocal n стало бы локальным в inc
n += 1
return n
return inc
Подводные камни
- Присваивание внутри функции делает имя локальным целиком, даже если используется выше по тексту — отсюда
UnboundLocalError. globalне работает для вложенной функции; для перезаписи в объемлющей нуженnonlocal.- Затенение builtins (
list = [...],id = ...,type = ...) ломает код модуля молча. - Класс не создаёт область enclosing для методов: метод не видит имена тела класса напрямую, только через
selfилиClassName.attr. - Comprehensions (кроме объектного
[x for x in ...]в Python 2) имеют собственную область — переменная цикла не утекает наружу в Python 3.
Common mistakes
- Путать enclosing и global.
- Не объяснять UnboundLocalError.
- Забывать builtins как последний уровень.
What the interviewer is testing
- Называет все четыре уровня.
- Объясняет влияние присваивания.
- Может предсказать поиск имени во вложенной функции.