Замыкания и правило LEGB
Как интерпретатор находит, на какую переменную ссылается имя, и что такое замыкание.
LEGB — порядок поиска имени: Local → Enclosing (объемлющая функция) → Global → Built-in. Замыкание — функция, которая помнит переменные объемлющей области.
Вопрос: как Python находит переменную по имени?
Чёткий ответ. Сначала смотрит в локальной области функции, затем в объемлющих функциях, затем в глобальной области модуля, и наконец среди встроенных имён. Первое найденное и используется.
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print("внутри inner:", x) # Local
inner()
print("внутри outer:", x) # Enclosing
outer()
print("в модуле:", x) # Global
Вывод:
внутри inner: local внутри outer: enclosing в модуле: global
Замыкание: функция помнит окружение
Вложенная функция может ссылаться на переменные объемлющей функции даже после того, как та завершилась. Эти переменные «захватываются» — получается замыкание.
def make_multiplier(factor):
def multiply(n):
return n * factor # factor взят из объемлющей области
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(10))
print(triple(10))
Вывод:
20 30
Каждый вызов make_multiplier создаёт своё замыкание со своим factor. double помнит 2, triple — 3.
nonlocal: изменить переменную объемлющей области
По умолчанию присваивание создаёт новую локальную переменную. Чтобы менять переменную объемлющей функции, нужен nonlocal; для глобальной — global.
def make_counter():
count = 0
def inc():
nonlocal count # без этого было бы UnboundLocalError
count += 1
return count
return inc
c = make_counter()
print(c())
print(c())
print(c())
Вывод:
1 2 3
Итог
- Имена ищутся по LEGB: Local → Enclosing → Global → Built-in.
- Замыкание — вложенная функция, помнящая переменные объемлющей области.
nonlocalпозволяет менять переменную объемлющей функции,global— глобальную.