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)nonlocal
palabra clave en Python 2 usando un objeto mutable, por ejemplo,L = [0] \n def counter(): L[0] += 1; return L[0]
es decir, no puede cambiar el nombre (vincularlo a otro objeto) en este caso, pero puede cambiar el objeto mutable en sí mismo al que se refiere el nombre a. La lista es obligatoria porque los enteros son inmutables en Python.