Замыкания и правило 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, triple3.

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 — глобальную.
Проверьте себя
1. Что означает LEGB?
AСписок встроенных функций
BПорядок поиска имени: Local → Enclosing → Global → Built-in
CТип области видимости
DКоманда импорта
2. Что такое замыкание (closure)?
AЗакрытый класс
BФункция, помнящая переменные объемлющей области после её завершения
CСпособ закрыть файл
DГлобальная переменная
3. Что нужно, чтобы изменять переменную объемлющей функции изнутри вложенной?
Aglobal
Bnonlocal
Cstatic
Dничего
Поддержать проект