В Python замыкание - это экземпляр функции, с которой постоянно связаны переменные.
Фактически, модель данных объясняет это в своем описании __closure__
атрибута функций :
Нет или набор ячеек, которые содержат привязки для свободных переменных функции. Только чтение
Чтобы продемонстрировать это:
def enclosure(foo):
def closure(bar):
print(foo, bar)
return closure
closure_instance = enclosure('foo')
Очевидно, мы знаем, что теперь у нас есть функция, на которую указывает имя переменной closure_instance
. Якобы, если мы вызываем его с помощью объекта, bar
он должен напечатать строку, 'foo'
и что бы там ни было строковое представление bar
.
На самом деле, строка «Foo» будет связан с экземпляром функции, и мы можем сразу прочитать его здесь, путем доступа к cell_contents
атрибуту (только и) первой ячейки в кортеже __closure__
атрибута:
>>> closure_instance.__closure__[0].cell_contents
'foo'
Кроме того, объекты ячеек описаны в документации C API:
Объекты "Ячейка" используются для реализации переменных, на которые ссылаются несколько областей видимости.
И мы можем продемонстрировать использование нашего закрытия, отметив, что 'foo'
оно застряло в функции и не меняется:
>>> closure_instance('bar')
foo bar
>>> closure_instance('baz')
foo baz
>>> closure_instance('quux')
foo quux
И ничего не может изменить:
>>> closure_instance.__closure__ = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: readonly attribute
Частичные функции
В данном примере замыкание используется как частичная функция, но если это наша единственная цель, ту же цель можно достичь с помощью functools.partial
>>> from __future__ import print_function
>>> partial_function = functools.partial(print, 'foo')
>>> partial_function('bar')
foo bar
>>> partial_function('baz')
foo baz
>>> partial_function('quux')
foo quux
Есть и более сложные замыкания, которые не подходят для примера частичной функции, и я продемонстрирую их дальше, когда позволит время.
nonlocal
был добавлен в python 3, в python 2.x не было полноценных закрытий для чтения-записи (то есть вы могли читать закрытые переменные, но не изменять их значения)