2009-05-08 18 views
5

¿Por qué este intento de crear una lista de funciones curried no funciona?¿Qué está pasando con la expresión lambda en esta función python?

def p(x, num): 
    print x, num 

def test(): 
    a = [] 
    for i in range(10): 
     a.append(lambda x: p (i, x)) 
    return a 

>>> myList = test() 
>>> test[0]('test') 
9 test 
>>> test[5]('test') 
9 test 
>>> test[9]('test') 
9 test 

¿Qué está pasando aquí?

Una función que realmente hace lo que espero la función anterior a hacer es:

import functools 
def test2(): 
    a = [] 
    for i in range (10): 
     a.append(functools.partial(p, i)) 
    return a 


>>> a[0]('test') 
0 test 
>>> a[5]('test') 
5 test 
>>> a[9]('test') 
9 test 
+0

Dado que tiene una solución que utiliza functools.partial, ¿cuál es la pregunta? –

+2

La pregunta es, ¿por qué no funciona el primer método? – David

Respuesta

12

En Python, las variables creadas en bucles y ramificaciones no están en el ámbito. Todas las funciones que está creando con lambda tienen una referencia a la misma variable i, que se establece en 9 en la última iteración del ciclo.

La solución es crear una función que devuelve una función, por lo que determina el alcance de la variable del iterador. Esta es la razón por la cual el enfoque functools.partial() funciona. Por ejemplo:

+0

Entendido. ¡Muchas gracias! – David

1

bien también se puede obligar a la i para un lambda exterior para los perezosos.

def p(x, num): 
    print x, num 

def test(): 
    a = [] 
    for i in range(10): 
     a.append((lambda i :lambda x: p (i, x))(i)) 
    return a 
1

Siempre estaba confundido de por qué esto no funciona. Gracias por la explicación, 'a paid nerd'. Yo personalmente prefiero esta solución:

for i in range(10): 
    a.append(lambda num, val_i=i: p (val_i, num)) 

Nota val_i=i el argumento predeterminado de la lambda que permite capturar el valor instantáneo de i durante el bucle mientras sigue haciendo efectivamente lambda una función de 1 variable. (Por cierto:. Cambiado su x en num para que coincida con p 's definición) me gusta más porque:

  1. que está muy cerca de la idea original y evita tener que definir una nueva función llamada, precisamente, con el propósito de una lambda ...
  2. evita la importación functools
  3. y evita lambdas de imbricación ...

acaba de hacer una búsqueda y encontrar explicaciones más detalladas para el mismo problema existe: Scope of python lambda functions and their parameters

+0

El argumento nombrado es por qué funciona y por qué su original no. Aquí está mi exploración de los cierres en Python: https://gist.github.com/maxcountryman/5035489 – maxcountryman

Cuestiones relacionadas