Trois lambdas différentes, un seul nombre. Le piège le plus classique des closures.Three different lambdas, one single number. The classic closure trap.
Le numéro précédent a établi LEGB : un nom lu dans une fonction est cherché en Local, puis Enclosing, puis Global, puis Built-in. Une closure est précisément une fonction qui lit un nom dans sa portée Enclosing — une variable définie dans la fonction (ou la boucle) qui l'englobe. Le piège qui ouvre ce numéro est le plus commenté de tout Python : construire une liste de fonctions dans une boucle, une par itération, en espérant que chacune se souvienne de la valeur de la boucle au moment de sa création. Elles s'en souviennent toutes — mais de la même, la dernière.The previous issue established LEGB: a name read inside a function is looked up in Local, then Enclosing, then Global, then Built-in. A closure is precisely a function that reads a name from its Enclosing scope — a variable defined in the function (or loop) that contains it. The trap opening this issue is the most commented-on in all of Python: building a list of functions inside a loop, one per iteration, hoping each remembers the loop's value at the moment of its creation. They all remember — but the same one, the last.
fonctions = [lambda: i for i in range(3)] print(fonctions[0]()) # 2 ! print(fonctions[1]()) # 2 ! print(fonctions[2]()) # 2 — les TROIS lambdas impriment la même chose # et pourtant : "i" valait 0, puis 1, puis 2, à trois moments différents # de la boucle. On s'attendait à 0, 1, 2. On obtient 2, 2, 2.
Face à une fonction créée dans une boucle qui lit une variable de la boucle, demande : cette fonction lit-elle la variable au moment où je la crée, ou au moment où je l'appelle ? La réponse, pour Python, est toujours la seconde.Facing a function created inside a loop that reads the loop's variable, ask: does this function read the variable at the moment I create it, or at the moment I call it? For Python, the answer is always the latter.
Le même piège se produit avec def à l'intérieur d'une boucle, avec un callback passé à un widget, avec une closure retournée par une fabrique de fonctions. lambda n'est qu'une syntaxe compacte pour créer une fonction — le comportement de capture est identique partout.The same trap occurs with def inside a loop, with a callback passed to a widget, with a closure returned by a function factory. lambda is just compact syntax for creating a function — the capture behavior is identical everywhere.