Замыкания Python: как использовать и почему?

В этом руководстве вы узнаете о замыкании 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

Интересные статьи...