2009-12-17 18 views
7

Teniendo en cuenta este fragmento de código:lambda Python y la determinación del alcance

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

yo esperaría que imprima [0, 1, 2], pero en su lugar se imprime [2, 2, 2]. ¿Hay algo fundamental que me falta acerca de cómo funcionan las lambdas con el alcance?

+2

pregunta común, se le preguntó (y respondió a) al menos una vez antes: http://stackoverflow.com/questions/233673/lexical-closures-in-python –

+0

@Vladimir: Yeap, pero el título de ambos es bastante diferente, nunca imaginaría que son más o menos lo mismo. – OscarRyz

Respuesta

8

Ésta es una pregunta frecuente en Python. Básicamente, el alcance es tal que cuando se llama a f(), utilizará 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.

5

x está vinculado al nivel de módulo x (que sobra 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'] 
4

Ya sabes la respuesta: sí. ;) Sin embargo, consuele, ya que este es un descubrimiento muy común para los pythonistas en ciernes. Cuando define una función o lambda que hace referencia a variables que no se "crean" dentro de esa función, crea un cierre sobre las variables. El efecto es que obtienes el valor de la variable al llamar a la función, no el valor en el momento de la definición. (Esperaba esto último)

Hay algunas formas de solucionar esto. En primer lugar está la unión 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] 
Cuestiones relacionadas