Recientemente comencé a jugar con Python y encontré algo peculiar en la forma en que funcionan los cierres. Considere el siguiente código:
adders=[0,1,2,3]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
Construye una matriz simple de funciones que toman una sola entrada y devuelven esa entrada agregada por un número. Las funciones se construyen en for
bucle donde el iterador i
se extiende desde 0
a 3
. Para cada uno de estos números lambda
, se crea una función que captura i
y agrega a la entrada de la función. La última línea llama a la segunda lambda
función con 3
como parámetro. Para mi sorpresa, la salida fue 6
.
Esperaba un 4
. Mi razonamiento fue: en Python todo es un objeto y, por lo tanto, cada variable es un puntero esencial. Al crear los lambda
cierres para i
, esperaba que almacenara un puntero al objeto entero al que apunta actualmente i
. Eso significa que cuando se le i
asigna un nuevo objeto entero no debería afectar los cierres creados anteriormente. Lamentablemente, la inspección de la adders
matriz dentro de un depurador muestra que sí. Todas las lambda
funciones se refieren al último valor de i
, 3
, que se traduce en adders[1](3)
regresar 6
.
Lo que me hace preguntarme sobre lo siguiente:
- ¿Qué capturan exactamente los cierres?
- ¿Cuál es la forma más elegante de convencer a las
lambda
funciones de capturar el valor actual dei
una manera que no se verá afectada cuandoi
cambie su valor?
i
deja el espacio de nombres?
print i
no funcionaría después del ciclo. Pero lo probé por mí mismo y ahora veo lo que quieres decir: funciona. No tenía idea de que las variables de bucle persistían después del cuerpo del bucle en Python.
if
, with
, try
etc.