English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Основной учебник Python

Контроль потока Python

Функции в Python

Типы данных в Python

Файловые операции Python

Объекты и классы Python

Дата и время Python

Высокий уровень знаний Python

Python справочник

Клوزуры в Python

В этой статье вы узнаете, что такое Python闭包, как определить闭包 и почему используется闭包.

Неявные переменные во вложенных функциях

Прежде чем понять, что такое闭包, мы должны сначала понять, что такое вложенные функции и неявные переменные.

функция, определённая в другой функции, называется вложенной функцией. Вложенная функция может обращаться к переменным ограниченного диапазона.

В Python по умолчанию эти переменные неявного диапазона являются только чтением, и我们必须 явно объявить их как неявные переменные (используяключевое слово nonlocal) для их изменения.

Вот пример вложенной функции, которая имеет доступ к переменным неявного диапазона.

def print_msg(msg):
# Это внешняя закрытая функция
    def printer():
# Это вложенная функция
        print(msg)
    printer()
# Мы выполняем эту функцию
# Вывод: Hello
print_msg("Hello")

Мы можем видеть, что вложенная функция printer() может обращаться к переменным неявного диапазона封闭ой функцииmsg.

Определение функции closures

Что会发生, если в последней строке функции print_msg() вместо её вызова будет возвращена функция printer()?

def print_msg(msg):
# Это внешняя закрытая функция
    def printer():
# Это вложенная функция
        print(msg)
    return printer # Это изменилось
# Теперь, давайте попробуем вызвать эту функцию.
# Вывод: Hello
another = print_msg("Hello")
another()

Это не очень необычно.

функция print_msg() вызывается строкой, "Hello", возвращающая функцию привязывается кдругойимя. При вызове another(), несмотря на то, что мы уже завершили выполнение функции print_msg(), мы всё ещё запоминаем это сообщение.

Эта техника, при которой к коду добавляется一些 данные ("Hello"),в PythonназываетсяClosure.

Даже если переменная выходит за пределы области или функция была удалена из текущего пространства имён, она запомнит это значение в ограниченном диапазоне.

Попробуйте выполнить следующие команды в Python Shell, чтобы увидеть результат.

>>> del print_msg
>>> another()
Hello
>>> print_msg("Hello")
Traceback (последний вызов в начале):
...
NameError: имя 'print_msg' не определено

Условия closures?

Из上面的 примера можно看出, в Python, когда вложенная функция ссылается на значение в своём ограниченном диапазоне, у нас есть closure.

Эти几点 резюмируют условия, которые необходимо выполнить для создания closures в Python.

  • У нас должен быть вложенная функция (функция в функции).

  • Вложенная функция должна ссылаться на значения, определённые в закрытой функции.

  • Закрытая функция должна возвращать вложенную функцию.

Когда использовать закрытый блок?

Так что, для чего нужен закрытый блок?

Закрытый блок может избежать использования глобальных значений и предоставить某种 форму скрытия данных. Он также может предложить面向 объектное решение для этой проблемы.

Когда в классе реализуется мало методов (в большинстве случаев это один метод), закрытый блок может предложить другую более изящную решение.

Это простой пример, где закрытый блок может быть более предпочтительным, чем определение класса и создание объектов.

def make_multiplier_of(n):
    def multiplier(x):
        return x * n
    return multiplier
# Множитель 3
times3 = make_multiplier_of(3)
# Множитель 5
times5 = make_multiplier_of(5)
# Вывод: 27
print(times3(9))
# Вывод: 15
print(times5(3))
# Вывод: 30
print(times5(times3(2)))

Декораторы в Python такжеМного использовалось闭包.

В конце концов, лучше отметить, что можно найти значения, заключенные в закрытых функциях.

У всех объектов функции есть атрибут __closure__, который возвращает кортеж объектов ячеек, если функция является закрытой функцией. По вышеуказанному примеру,我们知道 times3 и times5 являются закрытыми функциями.

>>> 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