Замыкания в Python: как вложенные функции захватывают переменные
Замыкания в Python
Узнайте, как работают замыкания в Python, как их определять и почему они необходимы для чистого, эффективного кода.
Переменные nonlocal во вложенной функции
Прежде чем изучать замыкания, важно понять вложенные функции и nonlocal-переменные. Функция, определённая внутри другой функции, называется вложенной. Такие функции могут читать переменные из объемлющей области видимости.
В Python эти nonlocal-переменные доступны только для чтения по умолчанию. Чтобы изменить их, нужно явно объявить переменную как nonlocal внутри вложенной функции.
Ниже — простой пример, показывающий вложенную функцию, обращающуюся к nonlocal-переменной.
def print_msg(msg):
# Внешняя функция
def printer():
# Вложенная функция
print(msg)
printer() # Выполнение
print_msg("Hello")
Вывод
Hello
Вложенная функция printer() может получить доступ к переменной msg из своей внешней области видимости.
Определение функции-замыкания
Что, если внешняя функция вернёт вложенную функцию вместо её вызова? Код выглядел бы так:
def print_msg(msg):
def printer():
print(msg)
return printer # Возврат вложенной функции
# Сохранение возвращённой функции
another = print_msg("Hello")
another()
Вывод
Hello
Обратите внимание: функция printer всё ещё помнит значение msg даже после завершения print_msg. Это явление — когда функция сохраняет доступ к переменным из своей области определения — называется замыканием в Python.
Для наглядности удалим исходную функцию и увидим, что возвращённая всё ещё работает:
>>> del print_msg
>>> another()
Hello
>>> print_msg("Hello")
Traceback (most recent call last):
...
NameError: name 'print_msg' is not defined
Возвращённая функция продолжает работать, потому что она захватила необходимые данные в своём замыкании.
Когда возникают замыкания?
В Python замыкание существует, когда вложенная функция:
- определена внутри другой функции;
- ссылается на переменную из своей объемлющей функции;
- возвращается внешней функцией.
Когда использовать замыкания?
Замыкания предлагают несколько преимуществ:
- Они устраняют необходимость в глобальных переменных, улучшая инкапсуляцию.
- Обеспечивают лёгкую альтернативу одно-методным классам, делая код лаконичнее.
- Позволяют функциональные паттерны, такие как декораторы, где обёртка нуждается в доступе к аргументам оригинальной функции.
Ниже — распространённый пример: фабрика, создающая функции-умножители.
def make_multiplier_of(n):
def multiplier(x):
return x * n
return multiplier
# Создание конкретных умножителей
times3 = make_multiplier_of(3)
times5 = make_multiplier_of(5)
print(times3(9)) # 27
print(times5(3)) # 15
print(times5(times3(2))) # 30
Вывод
27 15 30
Синтаксис декораторов Python сильно опирается на замыкания. Когда вы пишете декоратор, обёртка захватывает декорируемую функцию, образуя замыкание.
Для инспекции замыкания используйте атрибут __closure__. Он возвращает кортеж cell-объектов, содержащих захваченные значения. Например:
>>> make_multiplier_of.__closure__
>>> times3.__closure__
(<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)
Атрибут cell_contents каждой ячейки хранит фактическое значение, захваченное замыканием:
>>> times3.__closure__[0].cell_contents
3
>>> times5.__closure__[0].cell_contents
5
Понимание замыканий даёт возможность писать более чистый, модульный Python-код, избегая лишних глобальных переменных и принимая функциональные парадигмы.
Python
- Освоение типов данных в Python: практическое руководство
- Мастерство операторов Python: Полное руководство
- Оператор pass в Python: практическое руководство
- Lambda-функции в Python: практическое руководство по анонимным функциям
- Словари в Python: создание, манипуляции и продвинутые техники
- Генераторы в Python: эффективная итерация и продвинутые сценарии
- Декораторы в Python: улучшайте функции экспертными техниками
- Мастерство метода strip() в Python: подробное руководство и практические примеры
- Освоение лямбда-функций Python: практические примеры и лучшие практики
- Функция abs() в Python: вычисление абсолютных значений для чисел и комплексных чисел