Vol. 5 — № 15
The Python Loop · KiosqueNewsstand
Blog  
Un atelier Python · Édition d'ApprentissageA Python Workshop · Learning Edition

global & nonlocal : l'explicite contre l'implicite global & nonlocal: the explicit against the implicit

Lire un nom englobant ne demande aucune déclaration — LEGB s'en charge (numéro 13). Le réassigner en demande une : sans mot-clé, Python déclare un nom local tout neuf, et l'original englobant reste intouché. global et nonlocal lèvent cette ambiguïté, chacun pointant vers un scope différent — mais l'un crée silencieusement, l'autre exige que la case existe déjà. Reading an enclosing name needs no declaration — LEGB handles it (issue 13). Reassigning it needs one: without a keyword, Python declares a brand-new local name, and the original enclosing one stays untouched. global and nonlocal lift that ambiguity, each pointing to a different scope — but one creates silently, the other demands the slot already exists.

AudienceAudience
Dev dont le compteur enfermé dans une closure refuse obstinément de s'incrémenter Dev whose counter locked inside a closure stubbornly refuses to increment
Format
Self-paced
ChapitresChapters
5
Date
Juil 2026 Jul 2026
≈ 18 min ●●●○ PortéeClosuresglobalnonlocal

Chapitre 1 en accès libre — la suite (ch. 2 à 5) est réservée. Chapter 1 free to read — the rest (ch. 2–5) is members-only.

01CadrageFraming3 min

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.

Une lecture qui semblait anodine, une UnboundLocalErrorA read that looked harmless, an UnboundLocalError
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() !
Le sous-réflexe du numéroThis issue's sub-reflex

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.

Ce n'est pas une nouvelle UnboundLocalErrorThis isn't a new UnboundLocalError

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.

🔒

La suite est réservée The rest is members-only

Le premier numéro est libre. Débloque tout The Python Loop — tous les volumes, à vie — pour 5 €, paiement unique. The first issue is free. Unlock all of The Python Loop — every volume, forever — for €5, one-time.

Retour au kiosqueBack to newsstand