Question

Le test suivant échoue:

#!/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 d'autres termes:

>>> 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]
Était-ce utile?

La solution

Ils sont différents, car la valeur de i dans l'expression du générateur et dans la liste comp est évaluée paresseusement, c'est-à-dire lorsque les fonctions anonymes sont appelées dans f . < br> À ce moment-là, i est lié à la dernière valeur si t , qui est -1.

Donc, en gros, voici ce que la compréhension de liste fait (de même pour le genexp):

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

Les lambda portent maintenant une fermeture qui fait référence à i , mais i est lié à -1 dans les deux cas, car il s'agit de la dernière valeur à laquelle il a été attribué.

Si vous voulez vous assurer que le lambda reçoit la valeur actuelle de i , faites

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

De cette manière, vous forcez l'évaluation de i au moment de la création de la fermeture.

Modifier : il existe une différence entre les expressions du générateur et les compréhensions de liste: ces dernières filtrent la variable de boucle dans la portée environnante.

Autres conseils

Le lambda capture les variables, pas les valeurs, d’où le code

lambda : i

renverra toujours la valeur i est actuellement lié ??à la fermeture. Au moment où il est appelé, cette valeur a été définie sur -1.

Pour obtenir ce que vous voulez, vous devez capturer la liaison réelle au moment de la création du lambda, par:

>>> 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]

Expression f = lambda: i est équivalent à:

def f():
    return i

Expression g = lambda i = i: i est équivalent à:

def g(i=i):
    return i

i est une variable libre dans la première case et il est lié au paramètre function dans le second cas, c’est-à-dire qu’il s’agit d’une variable locale dans ce cas. Les valeurs des paramètres par défaut sont évaluées au moment de la définition de la fonction.

L'expression génératrice est la portée englobante la plus proche (où i est défini) pour le nom i dans l'expression lambda , donc i est résolu dans ce bloc:

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

i est une variable locale du bloc lambda i: ... . Par conséquent, l'objet auquel il fait référence est défini dans ce bloc:

f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top