Dans Python 2, la compréhension des listes fait "fuir" les variables vers la portée externe :
>>> [i for i in xrange(3)]
[0, 1, 2]
>>> i
2
Notez que le comportement est différent sur Python 3 :
>>> [i for i in range(3)]
[0, 1, 2]
>>> i
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'i' is not defined
Lorsque vous définissez un lambda, il est lié à une variable. i
et non sa valeur actuelle comme le montre votre deuxième exemple. Maintenant, lorsque vous attribuez une nouvelle valeur à i
le lambda retournera la valeur courante :
>>> a = [lambda: i for i in range(5)]
>>> a[0]()
4
>>> i = 'foobar'
>>> a[0]()
'foobar'
Puisque la valeur de i
dans la boucle est la lambda elle-même, vous l'obtiendrez comme valeur de retour :
>>> i = a[0]
>>> i()
<function <lambda> at 0x01D689F0>
>>> i()()()()
<function <lambda> at 0x01D689F0>
UPDATE : Exemple sur Python 2.7 :
Python 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [lambda: i for i in range(5)]
>>> for i in a:
... print i()
...
<function <lambda> at 0x7f1eae7f15f0>
<function <lambda> at 0x7f1eae7f1668>
<function <lambda> at 0x7f1eae7f16e0>
<function <lambda> at 0x7f1eae7f1758>
<function <lambda> at 0x7f1eae7f17d0>
Idem sur Python 3.4 :
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [lambda: i for i in range(5)]
>>> for i in a:
... print(i())
...
4
4
4
4
4
Pour plus de détails sur le changement concernant la portée des variables avec la compréhension des listes, voir l'article de Guido intitulé blogpost de 2010 .
Nous avons également apporté un autre changement dans Python 3, afin d'améliorer l'équivalence entre les compréhensions de listes et les expressions de générateur. Dans Python 2, la compréhension de liste "laisse échapper" la variable de contrôle de la boucle dans la portée environnante :
x = 'before'
a = [x for x in 1, 2, 3]
print x # this prints '3', not 'before'
Cependant, dans Python 3, nous avons décidé de corriger le "sale petit secret" des compréhensions de listes en utilisant la même stratégie de mise en œuvre que pour les expressions génératrices. Ainsi, en Python 3, l'exemple ci-dessus (après modification pour utiliser print(x) :-) affichera 'before', prouvant que le 'x' dans la compréhension de liste fait temporairement de l'ombre au 'x' dans la portée environnante mais ne le remplace pas.