Pregunta

La siguiente prueba falla:

#!/usr/bin/env python
def f(*args):
    """
    >>> t = 1, -1
    >>> f(*map(lambda i: lambda: i, t))
    [1, -1]
    >>> f(*(lambda: i for i in t)) # -> [-1, -1]
    [1, -1]
    >>> f(*[lambda: i for i in t]) # -> [-1, -1]
    [1, -1]
    """
    alist = [a() for a in args]
    print(alist)

if __name__ == '__main__':
    import doctest; doctest.testmod()

En otras palabras:

>>> t = 1, -1
>>> args = []
>>> for i in t:
...   args.append(lambda: i)
...
>>> map(lambda a: a(), args)
[-1, -1]
>>> args = []
>>> for i in t:
...   args.append((lambda i: lambda: i)(i))
...
>>> map(lambda a: a(), args)
[1, -1]
>>> args = []
>>> for i in t:
...   args.append(lambda i=i: i)
...
>>> map(lambda a: a(), args)
[1, -1]
¿Fue útil?

Solución

Son diferentes, porque el valor de i tanto en la expresión del generador como en la compilación de la lista se evalúa perezosamente, es decir, cuando las funciones anónimas se invocan en f . < br> En ese momento, i está vinculado al último valor si t , que es -1.

Entonces, básicamente, esto es lo que hace la comprensión de la lista (del mismo modo para el genexp):

x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)

Ahora las lambdas llevan un cierre que hace referencia a i , pero i está vinculado a -1 en ambos casos, porque ese es el último valor al que se le asignó.

Si desea asegurarse de que la lambda reciba el valor actual de i , haga

f(*[lambda u=i: u for i in t])

De esta manera, fuerza la evaluación de i en el momento en que se crea el cierre.

Editar : hay una diferencia entre las expresiones generadoras y las comprensiones de listas: estas últimas filtran la variable de bucle en el alcance circundante.

Otros consejos

La lambda captura variables, no valores, de ahí el código

lambda : i

siempre devolverá el valor al que está actualmente vinculado en el cierre. Para cuando se llama, este valor se ha establecido en -1.

Para obtener lo que desea, deberá capturar el enlace real en el momento en que se crea el lambda, mediante:

>>> f(*(lambda i=i: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda i=i: i for i in t]) # -> [-1, -1]
[1, -1]

Expresión f = lambda: i es equivalente a:

def f():
    return i

Expresión g = lambda i = i: i es equivalente a:

def g(i=i):
    return i

i es una variable libre en la primera case y está vinculado al parámetro de función en el segundo caso, es decir, es una variable local en ese caso. Los valores de los parámetros predeterminados se evalúan en el momento de la definición de la función.

La expresión

Generator es el ámbito de cierre más cercano (donde se define i ) para el nombre i en la expresión lambda , por lo tanto, i se resuelve en ese bloque:

f(*(lambda: i for i in (1, -1)) # -> [-1, -1]

i es una variable local del bloque lambda i: ... , por lo tanto, el objeto al que se refiere está definido en ese bloque:

f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top