El término "cierre" se ha tomado de la programación funcional y apareció en Python desde sus inicios. Los cierres permiten que una función recuerde el entorno en el que fue creada, incluso si se llama fuera de este entorno. Este concepto proporciona flexibilidad y permite implementar muchos patrones, incluidos fábricas de funciones y cálculos perezosos.
En Python, las funciones son objetos de primera clase. A veces es necesario que una función interna use variables del ámbito de la función externa, incluso después de que esta haya terminado. El ámbito léxico común no garantiza esto al devolver una función. Si tal función accede a las variables del entorno en el que fue creada, surge un cierre.
Un cierre se produce si la función interna hace referencia a las variables definidas en la externa, y esa externa devuelve la interna hacia fuera. Esto se usa a menudo para crear fábricas de funciones, encapsular estado sin clases y construir funciones con parámetros "in situ".
def make_multiplier(factor): def multiplier(x): return x * factor return multiplier mul2 = make_multiplier(2) mul3 = make_multiplier(3) print(mul2(10)) # 20 print(mul3(10)) # 30
nonlocal.¿Puede un cierre mantener un estado mutable entre llamadas si la variable se modifica dentro de la función interna?
Sí, si en la función interna se utiliza la palabra clave nonlocal. Sin nonlocal, la asignación crea una nueva variable local, sin modificar la externa.
def counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = counter() print(c()) # 1 print(c()) # 2
¿Se pueden implementar variables privadas en Python utilizando cierres en lugar de clases?
Sí, los cierres ofrecen una implementación sencilla de variables "privadas" que no son accesibles desde fuera, a menos que se proporcionen getters/setters en la función interna.
¿Los cierres se aplican solo a funciones? ¿Se puede organizar un cierre con lambdas en Python?
Sí, un cierre también puede formarse con expresiones lambda, ya que son análogas a def en la vinculación de variables léxicas.
def make_power(n): return lambda x: x ** n square = make_power(2) cube = make_power(3) print(square(4)) # 16 print(cube(2)) # 8
nonlocal.Una fábrica de funciones que forma manejadores en un bucle usa la variable del bucle dentro del cierre:
handlers = [] for i in range(3): def handler(x): return x + i handlers.append(handler) print([h(10) for h in handlers]) # [12, 12, 12]
Ventajas:
Desventajas:
i, cuyo último valor es 2 — comportamiento inesperado para la mayoría.Se utilizó un argumento por defecto para "fijar" el valor:
handlers = [] for i in range(3): def handler(x, j=i): return x + j handlers.append(handler) print([h(10) for h in handlers]) # [10, 11, 12]
Ventajas:
Desventajas: