2009-07-10 11 views
14

¿Qué está pasando aquí? Estoy tratando de crear una lista de funciones:Python Lambda Problems

def f(a,b): 
    return a*b 

funcs = [] 

for i in range(0,10): 
    funcs.append(lambda x:f(i,x)) 

Esto no está haciendo lo que esperaba. Yo esperaría que la lista para actuar de esta manera:

funcs[3](3) = 9 
funcs[0](5) = 0 

Pero todas las funciones de la lista parecen ser idénticos, y estar fijándose el valor fijo para ser 9:

funcs[3](3) = 27 
funcs[3](1) = 9 

funcs[2](6) = 54 

¿Alguna idea?

Respuesta

18

lambdas en Python son cierres .... los argumentos que le des aren' va a ser evaluado hasta que se evalúe la lambda. En ese momento, i = 9 independientemente, porque tu iteración ha terminado.

El comportamiento que usted está buscando se puede lograr con functools.partial

import functools 

def f(a,b): 
    return a*b 

funcs = [] 

for i in range(0,10): 
    funcs.append(functools.partial(f,i)) 
+0

Eso debería ser functools.partial (f, i) – FogleBird

+0

Estoy de acuerdo.La aplicación parcial es el camino a seguir aquí. –

+0

aquí parcial (f, i) significa parcial (f, b = i) no parcial (f, a = i). así que no es lo mismo que la publicación original. La aplicación de función parcial 'de la derecha' (http://www.gossamer-threads.com/lists/python/dev/715103) ha sido rechazada dos veces. – sunqiang

2

Teniendo en cuenta el coste final de i == 9

Como cualquier buena función pitón, que va a utilizar el valor de la variable en el ámbito que se define. ¿Quizás lambda: varname (siendo que es una construcción de lenguaje) se une al nombre, no al valor, y evalúa ese nombre en tiempo de ejecución?

similares a:

i = 9 
def foo(): 
    print i 

i = 10 
foo() 

estaría muy interesado en saber de mi respuesta es correcta

10

Sólo hay una i que está unido a cada uno de lambda, al contrario de lo que piensa. Este es un error común.

Una forma de obtener lo que desea es:

for i in range(0,10): 
    funcs.append(lambda x, i=i: f(i, x)) 

Ahora va a crear un parámetro por defecto i en cada cierre lambda y se une a ella el valor actual de la variable de bucle i.

13

Sí, el "problema de alcance" habitual (en realidad es un problema de vinculación posterior a la que desea, pero a menudo se denomina con ese nombre). Que ya ha conseguido los dos mejores respuestas (porque más simple) - el "default falsa" solución i=i y functools.partial, así que sólo estoy dando el tercero de los tres clásicos, el "lambda fábrica":

for i in range(0,10): 
    funcs.append((lambda i: lambda x: f(i, x))(i)) 

Personalmente iría con i=i si no hay riesgo de que se invoquen accidentalmente las funciones en funcs con 2 parámetros en lugar de solo 1, pero vale la pena considerar el enfoque de fábrica cuando necesita algo un poco más rico que solo vinculante uno arg.

+2

No he visto este antes. Tipo de ordenado. – ars

+0

@ars, es un poco exagerado para la mayoría de los casos (en Python), pero ES lo que uno naturalmente haría en, digamos, Scheme, así que supongo que es un poco limpio ;-). –

+0

Correcto, no puedo pensar de inmediato en un caso en el que lo prefiera, pero una construcción divertida, no obstante. :-) – ars