En Python, un cierre es una instancia de una función que tiene variables vinculadas a ella de manera inmutable.
De hecho, el modelo de datos explica esto en su descripción del __closure__
atributo de funciones :
Ninguna o una tupla de celdas que contienen enlaces para las variables libres de la función. Solo lectura
Para demostrar esto:
def enclosure(foo):
def closure(bar):
print(foo, bar)
return closure
closure_instance = enclosure('foo')
Claramente, sabemos que ahora tenemos una función apuntada desde el nombre de la variable closure_instance
. Aparentemente, si lo llamamos con un objeto, bar
debería imprimir la cadena, 'foo'
y cualquiera que sea la representación de la cadena bar
.
De hecho, la cadena 'foo' está vinculada a la instancia de la función, y podemos leerla directamente aquí, accediendo al cell_contents
atributo de la primera (y única) celda en la tupla del __closure__
atributo:
>>> closure_instance.__closure__[0].cell_contents
'foo'
Además, los objetos de celda se describen en la documentación de la API de C:
Los objetos de "celda" se utilizan para implementar variables a las que hacen referencia varios ámbitos
Y podemos demostrar el uso de nuestro cierre, notando que 'foo'
está atascado en la función y no cambia:
>>> closure_instance('bar')
foo bar
>>> closure_instance('baz')
foo baz
>>> closure_instance('quux')
foo quux
Y nada puede cambiarlo:
>>> closure_instance.__closure__ = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: readonly attribute
Funciones parciales
El ejemplo dado usa el cierre como una función parcial, pero si este es nuestro único objetivo, el mismo objetivo se puede lograr con 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
También hay cierres más complicados que no encajarían en el ejemplo de función parcial, y los demostraré más a medida que el tiempo lo permita.
nonlocal
se agregó en python 3, python 2.x no tenía cierres completos de lectura y escritura (es decir, podría leer variables cerradas, pero no cambiar sus valores)