ProgramaciónDesarrollador Backend

Explique cómo funcionan los cierres (closures) en Python, en qué se diferencian de las funciones comunes y cuál es su aplicación práctica?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta

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.

Problema

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.

Solución

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".

Ejemplo de código:

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

Características clave:

  • El cierre guarda los valores de las variables del entorno, incluso si la función externa ya ha terminado.
  • El estado en la función interna es esencialmente privado, no se puede modificar directamente desde fuera.
  • Para modificar una variable no inmutable en la función externa dentro del cierre, se utiliza la palabra clave nonlocal.

Preguntas engañosas.

¿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

Errores comunes y anti-patrones

  • Esperar que el cierre modifique automáticamente la variable externa al cambiarla dentro sin nonlocal.
  • Capturar objetos mutables en un cierre y mutarlos sin darse cuenta de las complicaciones en la depuración.
  • Usar un bucle para crear funciones en un cierre sin vincular correctamente las variables (trampa "todas las funciones ven el último valor de la variable").

Ejemplo de la vida real

Caso negativo

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:

  • Sencillo, poco código.

Desventajas:

  • Todos los manejadores hacen referencia a la misma variable i, cuyo último valor es 2 — comportamiento inesperado para la mayoría.

Caso positivo

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:

  • Vinculación del valor necesario.
  • Comportamiento predecible.

Desventajas:

  • Es necesario recordar este matiz y corregir el cierre manualmente, lo que complica el mantenimiento del código.