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