ProgrammationDéveloppeur Python

Qu'est-ce que la 'règle LEGB' en Python ? Comment cela fonctionne-t-il lors de la recherche d'un nom de variable ? Donnez des exemples de situations où une mauvaise compréhension de cette règle conduit à des bogues non triviaux.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Règle LEGB — c'est la règle de recherche des espaces de noms (scoping) en Python. L'acronyme se décompose comme suit :

  • Local — espace de noms local (dans une fonction);
  • Enclosing — espaces de noms de toutes les fonctions englobantes;
  • Global — espace de module (variables globales);
  • Builtin — noms intégrés de Python (len, list, etc.).

Lorsque Python rencontre une variable, il cherche d'abord localement, puis dans les fonctions englobantes, ensuite dans l'espace global du module, et enfin dans l'espace des objets intégrés :

def outer(): x = 'enclosing' def inner(): x = 'local' print(x) inner() outer() # 'local'

Si à l'intérieur de inner() on supprime l'assignation de x, Python prendra x de l'espace englobant (outer). S'il n'y a pas de variable là non plus, il cherche globalement, puis dans les intégrés.


Question piège.

Comment se comportera la fonction si vous oubliez de déclarer nonlocal ou global lors de la modification d'une variable d'un niveau supérieur ?

Réponse : Si vous essayez de modifier une variable de l'espace d'un niveau supérieur à l'intérieur d'une fonction imbriquée sans déclarer nonlocal (ou global — si la variable est au niveau du module), Python créera une nouvelle variable locale, sans modifier l'extérieur.

Exemple :

x = 10 def foo(): x = 20 # Ceci est un nouveau x local, le global ne change pas foo() print(x) # 10

Pour modifier le x global :

def foo(): global x x = 20

Avec des variables dans la fonction englobante — utilisez nonlocal.


Exemples d'erreurs réelles dues à la méconnaissance des subtilités du sujet.


Histoire 1

Dans un projet éducatif, des fonctions imbriquées ont été utilisées pour compter le nombre d'appels. Le développeur a écrit :

def counter(): count = 0 def inc(): count += 1 return count return inc

Mais cela a entraîné un UnboundLocalError, car count à l'intérieur de inc() était considéré comme nouveau (un count local était créé). Solution : déclarer count nonlocal.


Histoire 2

Dans une application web, ils voulaient modifier le cache global dans le gestionnaire :

cache = {} def add_to_cache(key, value): cache[key] = value

Cependant, si on avait essayé de réécrire complètement le cache à l'intérieur de la fonction, par exemple cache = {key: value}, cela aurait créé un nouveau cache local, sans lien avec le global, en raison de l'absence de global cache.


Histoire 3

Dans un grand système ETL, un bogue a été rencontré : les programmeurs ont laissé la variable result sans initialisation explicite à l'intérieur de la fonction, espérant qu'elle se prendrait de l'espace externe. Mais après quelques itérations, le résultat a commencé à être décalé, car à l'intérieur il y avait une écriture result += value, et non result = result + value (sans nonlocal/global) — et result était réinitialisé à chaque appel.