Los closures son un mecanismo poderoso en Python que permite crear funciones con datos "recordados" del contexto circundante.
En lenguajes funcionales y en Python, las funciones son primeros ciudadanos. Una función puede devolver una nueva función que "cierra" las variables de su ámbito (externo).
Los desarrolladores a menudo se confunden sobre cómo exactamente viven las variables cerradas dentro de un closure, cómo se leen o escriben, y cómo trabajar con ellas de manera segura (especialmente con objetos mutables).
Si una función interna utiliza variables de la función externa, automáticamente se "recuerdan" — incluso si la función externa ya ha terminado de ejecutarse. Para leer una variable todo es evidente, pero si es necesario cambiar el valor — se requiere usar la palabra clave nonlocal. Al trabajar con objetos mutables (listas, diccionarios) esto es una zona de riesgo especial.
Ejemplo:
def outer(): count = 0 def inner(): nonlocal count count += 1 return count return inner counter = outer() print(counter()) # 1 print(counter()) # 2
Características clave:
nonlocal (de lo contrario, la operación crea una variable local).¿Se puede cambiar el valor de una variable cerrada dentro de una función sin nonlocal?
No. Si intentas asignar un nuevo valor sin indicar nonlocal, Python lo interpreta como la creación de una nueva variable local, y el antiguo valor no saldrá al exterior.
Ejemplo:
def make_counter(): count = 0 def inner(): count += 1 # Error UnboundLocalError! return count return inner
¿Se pueden pasar argumentos al ámbito exterior a través de un closure?
Sí, un closure "recordará" cualquier variable accesible en el ámbito externo, incluidos atributos de clases, variables globales, etc. Pero modificar estas variables requiere esfuerzos especiales (por ejemplo, el uso de nonlocal o global).
¿Qué sucede con los objetos mutables dentro de un closure?
Si una variable cerrada es una referencia a un objeto mutable, por ejemplo, una lista, puedes modificar su contenido sin nonlocal, pero si intentas reasignar la variable, necesitarás nonlocal.
Ejemplo:
def make_appender(): result = [] def append(x): result.append(x) # ¡Es posible! return result return append f = make_appender() print(f(1)) # [1] print(f(2)) # [1, 2]
nonlocal.Un desarrollador escribe un closure, modifica una variable sin nonlocal — se produce un UnboundLocalError.
Ventajas:
Desventajas:
nonlocal.Uso explícito de nonlocal para un estado controlado en un closure.
Ventajas:
Desventajas: