Le compteur qui refuse de compter : réassigner un nom englobant ne va pas de soi.The counter that refuses to count: reassigning an enclosing name isn't automatic.
Le numéro précédent s'est arrêté sur une lecture : une closure qui consulte une variable englobante n'a besoin d'aucune déclaration — LEGB s'en charge silencieusement. Mais vouloir réassigner cette même variable depuis l'intérieur change tout. incrementer() veut seulement ajouter 1 à compte — et pourtant Python refuse dès le premier appel, sur la lecture que contient compte += 1. La raison tient en une règle déjà posée au numéro 13 : une assignation, où qu'elle soit dans le corps d'une fonction, classe le nom comme local pour toute la fonction. compte += 1 est une assignation. Elle rend compte local à incrementer() — un nom local qu'on lit avant de l'avoir jamais assigné.The previous issue stopped at a read: a closure consulting an enclosing variable needs no declaration — LEGB handles it silently. But wanting to reassign that same variable from the inside changes everything. incrementer() only wants to add 1 to compte — and yet Python refuses on the very first call, on the read hidden inside compte += 1. The reason is a rule already laid down in issue 13: an assignment, wherever it sits in a function's body, classifies the name as local for the whole function. compte += 1 is an assignment. It makes compte local to incrementer() — a local name read before it was ever assigned.
def fabrique_compteur(): compte = 0 def incrementer(): compte += 1 # compte += 1 == compte = compte + 1 -> une ASSIGNATION return compte return incrementer c = fabrique_compteur() c() # UnboundLocalError: cannot access local variable 'compte' # where it is not associated with a value # "compte" est pourtant juste au-dessus, dans fabrique_compteur() !
Face à une fonction imbriquée qui doit modifier — pas seulement lire — un nom vivant dans une portée englobante, demande : ce nom vit-il dans le module (Global), ou dans la fonction qui contient celle-ci (Enclosing) ? La réponse fixe le mot-clé à utiliser.Facing a nested function that must modify — not just read — a name living in an enclosing scope, ask: does this name live in the module (Global), or in the function that contains this one (Enclosing)? The answer fixes which keyword to use.
C'est exactement le même mécanisme que le numéro 13 — une variable classée locale par une assignation, lue avant sa première affectation. Seule différence : la variable qu'on croyait pouvoir réassigner sans effort n'était pas globale, mais une portée plus loin, dans une closure.It's exactly the same mechanism as issue 13 — a variable classified local by an assignment, read before its first binding. The only difference: the variable you thought you could reassign freely wasn't global, but one scope further out, inside a closure.