Термин "замыкание" был заимствован из функционального программирования и появился в Python с самого начала. Замыкания позволяют функции запоминать окружение, в котором они были созданы, даже если вызываются вне этого окружения. Эта концепция обеспечивает гибкость и позволяет реализовать многие паттерны, в том числе фабрики функций и ленивые вычисления.
В Python функции — объекты первого класса. Иногда требуется, чтобы вложенная функция использовала переменные из области видимости enclosing-функции даже после её завершения. Обычная лексическая область видимости этого не гарантирует при возврате функции. Если такая функция обращается к переменным среды своего создания — возникает замыкание.
Замыкание возникает, если внутренняя функция ссылается на переменные, определённые во внешней, а та внешняя возвратила внутреннюю вовне. Это часто используется для создания фабрик функций, инкапсуляции состояния без классов и конструирования функции с параметрами "по месту".
def make_multiplier(factor): def multiplier(x): return x * factor return multiplier mul2 = make_multiplier(2) mul3 = make_multiplier(3) print(mul2(10)) # 20 print(mul3(10)) # 30
nonlocal.Может ли замыкание сохранять изменяемое состояние между вызовами, если переменная изменяется внутри вложенной функции?
Да, если во вложенной функции воспользоваться ключевым словом nonlocal. Без nonlocal присваивание создаёт новую локальную переменную, не изменяя внешнюю.
def counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = counter() print(c()) # 1 print(c()) # 2
Можно ли реализовать приватные переменные в Python, используя замыкания, вместо классов?
Да, closure предлагает простую реализацию "приватных" переменных, недоступных извне, если не предоставлены геттеры/сеттеры во вложенной функции.
Применяются ли замыкания только к функциям? Можно ли организовать closure с лямбдами в Python?
Да, замыкание может образовываться и с лямбда-выражениями, поскольку они аналогичны def по привязке лексических переменных.
def make_power(n): return lambda x: x ** n square = make_power(2) cube = make_power(3) print(square(4)) # 16 print(cube(2)) # 8
nonlocal.Фабрика функций, формирующая обработчики в цикле, использует переменную цикла внутри closure:
handlers = [] for i in range(3): def handler(x): return x + i handlers.append(handler) print([h(10) for h in handlers]) # [12, 12, 12]
Плюсы:
Минусы:
i, её последнее значение 2 — поведение неожиданное для большинства.Использован аргумент по умолчанию, чтобы "зафиксировать" значение:
handlers = [] for i in range(3): def handler(x, j=i): return x + j handlers.append(handler) print([h(10) for h in handlers]) # [10, 11, 12]
Плюсы:
Минусы: