Récemment, j'ai commencé à jouer avec Python et j'ai trouvé quelque chose de particulier dans le fonctionnement des fermetures. Considérez le code suivant:
adders=[0,1,2,3]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
Il crée un tableau simple de fonctions qui prennent une seule entrée et retournent cette entrée ajoutée par un nombre. Les fonctions sont construites en for
boucle où l'itérateur i
va de 0
à 3
. Pour chacun de ces nombres, une lambda
fonction est créée qui la capture i
et l'ajoute à l'entrée de la fonction. La dernière ligne appelle la deuxième lambda
fonction avec 3
comme paramètre. À ma grande surprise, la sortie était 6
.
Je m'attendais à un 4
. Mon raisonnement était: en Python, tout est un objet et donc chaque variable est un pointeur essentiel pour lui. Lors de la création des lambda
fermetures pour i
, je m'attendais à ce qu'il stocke un pointeur sur l'objet entier actuellement pointé par i
. Cela signifie que lorsqu'il est i
affecté à un nouvel objet entier, il ne doit pas affecter les fermetures précédemment créées. Malheureusement, l'inspection du adders
tableau dans un débogueur montre que c'est le cas. Toutes les lambda
fonctions se rapportent à la dernière valeur i
, 3
dont les résultats en adders[1](3)
retour 6
.
Ce qui me fait me demander ce qui suit:
- Que capturent exactement les fermetures?
- Quelle est la façon la plus élégante de convaincre les
lambda
fonctions de capturer la valeur actuelle d'i
une manière qui ne sera pas affectée lors d'uni
changement de valeur?
i
quitte l'espace de noms?
print i
cela ne fonctionnerait pas après la boucle. Mais je l'ai testé par moi-même et maintenant je vois ce que vous voulez dire - cela fonctionne. Je n'avais aucune idée que les variables de boucle persistaient après le corps de la boucle en python.
if
, with
, try
etc.