В этом руководстве вы узнаете о замыкании Python, о том, как определить замыкание, и о причинах, по которым вы должны его использовать.
Нелокальная переменная во вложенной функции
Прежде чем перейти к тому, что такое замыкание, мы должны сначала понять, что такое вложенная функция и нелокальная переменная.
Функция, определенная внутри другой функции, называется вложенной функцией. Вложенные функции могут обращаться к переменным охватывающей области.
В Python эти нелокальные переменные по умолчанию доступны только для чтения, и мы должны явно объявить их нелокальными (используя ключевое слово nonlocal), чтобы изменить их.
Ниже приведен пример вложенной функции, обращающейся к нелокальной переменной.
def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) printer() # We execute the function # Output: Hello print_msg("Hello")
Вывод
Здравствуйте
Мы видим, что вложенная printer()
функция смогла получить доступ к нелокальной переменной msg включающей функции.
Определение функции закрытия
Что произойдет в приведенном выше примере, если последняя строка функции print_msg()
вернет printer()
функцию вместо ее вызова? Это означает, что функция была определена следующим образом:
def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) return printer # returns the nested function # Now let's try calling this function. # Output: Hello another = print_msg("Hello") another()
Вывод
Здравствуйте
Это необычно.
print_msg()
Функция была вызвана строкой "Hello"
и возвращаемая функция была связана с именем другого. При вызове another()
сообщение все еще запоминалось, хотя мы уже завершили выполнение print_msg()
функции.
Этот метод, с помощью которого некоторые данные ( "Hello
в данном случае) прикрепляются к коду, в Python называется закрытием .
Это значение в охватывающей области видимости запоминается, даже когда переменная выходит за пределы области видимости или сама функция удаляется из текущего пространства имен.
Попробуйте запустить следующее в оболочке Python, чтобы увидеть результат.
>>> del print_msg >>> another() Hello >>> print_msg("Hello") Traceback (most recent call last):… NameError: name 'print_msg' is not defined
Здесь возвращенная функция по-прежнему работает, даже если исходная функция была удалена.
Когда у нас закрытие?
Как видно из приведенного выше примера, у нас есть закрытие в Python, когда вложенная функция ссылается на значение в своей охватывающей области.
Критерии, которые должны быть соблюдены для создания замыкания в Python, суммированы в следующих пунктах.
- У нас должна быть вложенная функция (функция внутри функции).
- Вложенная функция должна ссылаться на значение, определенное во включающей функции.
- Включающая функция должна возвращать вложенную функцию.
Когда использовать закрытие?
Итак, для чего нужны закрытия?
Замыкания позволяют избежать использования глобальных значений и обеспечивают некоторую форму сокрытия данных. Он также может предоставить объектно-ориентированное решение проблемы.
Когда есть несколько методов (в большинстве случаев один метод), которые необходимо реализовать в классе, замыкания могут предоставить альтернативное и более элегантное решение. Но когда количество атрибутов и методов становится больше, лучше реализовать класс.
Вот простой пример, в котором замыкание может быть более предпочтительным, чем определение класса и создание объектов. Но предпочтение остается за вами.
def make_multiplier_of(n): def multiplier(x): return x * n return multiplier # Multiplier of 3 times3 = make_multiplier_of(3) # Multiplier of 5 times5 = make_multiplier_of(5) # Output: 27 print(times3(9)) # Output: 15 print(times5(3)) # Output: 30 print(times5(times3(2)))
Вывод
27 15 30
Декораторы Python также широко используют замыкания.
В заключение стоит отметить, что значения, заключенные в функцию закрытия, можно узнать.
Все функциональные объекты имеют __closure__
атрибут, который возвращает кортеж объектов ячеек, если это функция закрытия. Обращаясь к приведенному выше примеру, мы знаем times3
и times5
являемся закрывающими функциями.
>>> make_multiplier_of.__closure__ >>> times3.__closure__ (,)
Объект ячейки имеет атрибут cell_contents, в котором хранится закрытое значение.
>>> times3.__closure__(0).cell_contents 3 >>> times5.__closure__(0).cell_contents 5