Prépa Python

Expert — chapitre 44 sur 54

Scopes, namespaces et closures

La règle LEGB et les subtilités de la portée des variables

75 minAbonnés

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}.

python
# Le namespace local d'une fonction peut être inspecté
def f():
    a = 1
    b = 2
    print(locals())     # dict des variables locales

f()
python
# 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

python
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}")                   # G

Chaque 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.

python
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).

python
# 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

python
x = 10      # global

def f():
    print(x)     # lit x depuis le global : OK

f()

Écrire : crée une variable locale

python
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

python
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 :

python
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) :

python
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())       # 3

nonlocal ne peut viser qu'une variable de fonction englobante, pas le global.

Suite réservée aux abonnés

Débloquez l'intégralité du parcours

Les 54 chapitres, les exercices corrigés automatiquement, les examens blancs et le suivi de votre progression.

Voir les offresChapitres en accès libre

Déjà un accès ? Connectez-vous.