Quand vous écrivez x = 10, où cette variable x existe-t-elle ? Dans la fonction ? Dans le module ? Ailleurs ? Python suit une règle précise, la règle LEGB, pour résoudre les noms. Maîtriser cette règle évite des bugs mystérieux et permet d'écrire des closures et décorateurs solides. C'est un sujet fondamental au niveau Expert.
Qu'est-ce qu'un namespace ?
Un namespace (espace de noms) est simplement un dictionnaire qui associe des noms (variables, fonctions, classes) à des objets.
Quand vous écrivez x = 10, Python ajoute une entrée dans le namespace courant : {"x": 10}.
# Le namespace local d'une fonction peut être inspecté
def f():
a = 1
b = 2
print(locals()) # dict des variables locales
f()# Le namespace global aussi
pays = "France"
langue = "Python"
# globals() renvoie le dict du namespace global (filtré pour affichage)
noms_interessants = {k: v for k, v in globals().items() if not k.startswith("_")}
# (On affiche juste les clés pertinentes)Python maintient plusieurs namespaces simultanément, dans une hiérarchie.
La règle LEGB
Quand Python rencontre un nom (comme x), il le cherche dans cet ordre :
| Niveau | Nom | Où |
|---|---|---|
| L | Local | Variables locales de la fonction courante |
| E | Enclosing | Variables des fonctions englobantes |
| G | Global | Variables du module |
| B | Built-in | Fonctions/noms prédéfinis de Python (print, len, int...) |
Dès qu'il trouve, il s'arrête. Si rien ne correspond, c'est NameError.
Illustration complète
x = "global" # G : global
def externe():
x = "enclosing" # E : englobante
def interne():
x = "local" # L : local
print(f"Dans interne : {x}") # L trouvé d'abord
interne()
print(f"Dans externe : {x}") # E
externe()
print(f"Global : {x}") # GChaque fonction a son propre scope local qui cache les noms identiques des niveaux supérieurs.
Accéder aux niveaux supérieurs
Si un nom n'existe pas en local, Python remonte la chaîne.
x = "global"
def fonction():
# Pas de x local → remonte au global
print(x)
fonction()Les built-ins
Les fonctions comme print, len, max, int... sont dans le niveau B (built-in).
# print est trouvé au niveau B
print("Hello")
# On peut le redéfinir (mauvaise idée !)
print = "pas une fonction"
# Restaurons
del print
print("Normal")Lire vs écrire une variable
Dans une fonction, Python distingue fortement lecture et écriture.
Lire : remonte la chaîne LEGB
x = 10 # global
def f():
print(x) # lit x depuis le global : OK
f()Écrire : crée une variable locale
x = 10 # global
def f():
x = 20 # crée une variable LOCALE x (n'affecte pas le global)
print(f"Dans f : x = {x}")
f()
print(f"Global : x = {x}") # inchangé !C'est le point le plus subtil. Dès que Python voit x = ... dans une fonction, il considère x comme local à cette fonction. Pas de question.
Le piège du mixte
x = 10
def f():
print(x) # tentative de lecture AVANT affectation
x = 20
f()UnboundLocalError : puisque x = 20 apparaît dans f, Python a classé x comme local pour toute la fonction. Le print(x) initial cherche donc x local... qui n'a pas encore été créé.
Modifier une variable externe : global et nonlocal
global : accéder au niveau module
Pour modifier une variable globale depuis une fonction :
compteur = 0
def incrementer():
global compteur # dit à Python : pas de local, utilise le global
compteur += 1
incrementer()
incrementer()
incrementer()
print(f"Compteur : {compteur}")Sans global, Python aurait créé une nouvelle variable locale compteur.
nonlocal : accéder au niveau enclosing
Pour modifier une variable d'une fonction englobante (pas globale) :
def compteur():
n = 0
def increment():
nonlocal n # accès à n de compteur (pas global !)
n += 1
return n
return increment
c = compteur()
print(c()) # 1
print(c()) # 2
print(c()) # 3nonlocal ne peut viser qu'une variable de fonction englobante, pas le global.