Python lambdas y scope

Dado este fragmento de código:

funcs = [] for x in range(3): funcs.append(lambda: x) print [f() for f in funcs] 

Espero que se imprima [0, 1, 2] , pero en su lugar imprime [2, 2, 2] . ¿Hay algo fundamental que me esté perdiendo sobre cómo funcionan las lambdas con el scope?

Esta es una pregunta frecuente en Python. Básicamente, el scope es tal que cuando se llama f() , usará el valor actual de x , no el valor de x en el momento en que se forma la lambda. Hay una solución estándar:

 funcs = [] for x in range(10): funcs.append(lambda x=x: x) print [f() for f in funcs] 

El uso de lambda x = x recupera y guarda el valor actual de x .

x está vinculado al nivel de módulo x (que queda del bucle for).

Un poco más claro:

 funcs = [] for x in range(10): funcs.append(lambda: x) x = 'Foo' print [f() for f in funcs] # Prints ['Foo', 'Foo', 'Foo', 'Foo', 'Foo', 'Foo', 'Foo', 'Foo', 'Foo', 'Foo'] 

Usted sabe la respuesta: sí. 😉 Tenga consuelo, sin embargo, ya que este es un descubrimiento muy común para los pitonistas en ciernes. Cuando define una función o lambda que hace referencia a variables no “creadas” dentro de esa función, se crea un cierre sobre las variables. El efecto es que obtiene el valor de la variable al llamar a la función, no el valor en el momento de la definición. (Estabas esperando lo último.)

Hay algunas maneras de lidiar con esto. Lo primero es vincular las variables extra:

 funcs = [] for x in range(10): funcs.append(lambda x=x: x) print [f() for f in funcs] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

La segunda forma es un poco más formal:

 from functools import partial funcs = [] for x in range(10): funcs.append(partial(lambda x: x, x)) print [f() for f in funcs] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]