L’espace de noms local d’une fonction

Quand une fonction f est appelée (et que débute son évaluation), elle crée un nouvel espace de noms : cela signifie que les variables qui sont créées dans cette fonction ne sont visibles qu’à l’intérieur de celle-ci (elles y sont locales). Notre fonction f peut cependant continuer à accéder aux variables de l’espace global. De même, si notre fonction f contient la définition d’une fonction g, celle-ci crée un espace de noms inclus dans celui de f .

On peut lire les définitions locales d’une fonction en évaluant l’expression locals().

Le mieux est de prendre quelques exemples, après avoir redémarré l’interpréteur Python.

On définit ensuite une fonction f (prenant deux arguments x et y) à l’intérieur de laquelle on définit (puis on appelle) une fonction g (prenant un argument z).

Les deux fonctions f et g créent des variables et affichent, à plusieurs reprises, le contenu de l’expression locals() (pour qu’on puisse retracer l’évolution de l’espace de nom local en cours).

On accompagne cet affichage d’une lettre majuscule (de ’A’ à ’G’) pour mieux suivre la chronologie.

À un moment donné, à l’intérieur de la fonction g, on demande également l’affichage du dictionnaire de l’espace de noms global.

On commence par rappeler le contenu du dictionnaire de l’espace de noms global (on y trouve bien sûr un enregistrement relatif à la fonction f ). On voit à cette occasion que dans ce dictionnaire le contenu de la variable f est représenté par “function f at...”, avec indication de l’adresse du code de la fonction en mémoire.

On appelle maintenant f avec les arguments 10 et 20. On observe (et on commente après) les affichages obtenus :

Voici comment on peut interpréter les résultats précédents :

A : on vient d’entrer dans la fonction f , et celle-ci a créé deux variables locales x et y, qui ont reçu respectivement les valeurs 10 et 20 passées lors de l’appel à f .

B : dans la fonction f , on a créé une variable locale a (et on lui a donné la valeur 3).

F : on est ici juste après la définition de g à l’intérieur de f . On voit que g a été ajoutée au dictionnaire de l’espace de noms local à f , et la description du contenu de g (à savoir <function f.<locals>.g at 0x1040b8680>) indique bien qu’il s’agit ici d’une fonction n’existant que dans l’espace de noms local à f .

C : on vient d’entrer dans la fonction g (appelée avec l’argument 6). La fonction g a donc créé une variable locale z avec la valeur 6. On voit que l’espace de noms local est ici celui de g, et qu’il se réduit pour l’instant à la variable z.

D : La fonction g vient de créer deux variables locales a et b, avec les valeurs 4 et 5.

E : Depuis la fonction g on a demandé l’affichage du dictionnaire de l’espace de noms global. On y retrouve les enregistrements relatifs à f et aux variables globales a et b (définies avant l’appel de f ) : on voit qu’elles ont conservé les valeurs 1 et 2, et qu’elles ne sont donc pas affectées par les définitions locales de a et de b dans l’espace de g.

G : On est maintenant “sorti” de la fonction g, et on retrouve le dictionnaire de nom local à la fonction f . On voit notamment que les variables locales z, a, b définies dans g n’existent plus : l’enregistrement relatif à la variable a évoque en fait la variable locale définie dans f par l’instruction a = 3.

Quand cet appel à la fonction f est terminé, on revient au niveau interactif (global) et on retrouve le dictionnaire de l’espace de nom global dans l’état où il était avant l’évaluation de l’expression f (10, 20). On y retrouve notamment les deux variables globales a = 1 et b = 2 :

On voit maintenant une situation différente, dans laquelle les variables concernées ont un contenu de type liste (donc un contenu composite et mutable).

On redémarre Python, et on définit les fonctions f, g ci-contre. On place la liste [1, 2, 3] dans la variable globale a.

Avec les définitions précédentes, on évalue l’expression f (a). On voit (affichage ’A’) que la fonction f crée la variable locale x et qu’elle y place le contenu de a. En fait, à cet instant, les deux variables a (globale) et x (locale) pointent sur une liste unique à une adresse bien précise en mémoire (celle donnée par la fonction id).

Ensuite, on redéfinit la variable locale x, en y plaçant [4, 5, 6]. On voit (affichage ’B’) que les deux variables a et x pointent sur deux listes différentes en mémoire. C’est normal, car les valeurs sont différentes, mais le résultat aurait été le même si on avait écrit x = [1, 2, 3] : on aurait défini une nouvelle liste en mémoire (égalité structurelle mais pas égalité physique).

On évalue maintenant l’expression g(a). On voit (affichage ’C’) que g crée la variable locale x et qu’elle y place le contenu de a. À ce moment il y a égalité physique entre les deux. Ensuite l’instruction x[0] = 9 ne redéfinit par la liste x, mais seulement l’un de ses éléments, et cela ne modifie pas l’adresse de la liste x en mémoire (ça modifie seulement l’adresse de la valeur correspondant à la première position de cette liste).

On a la confirmation de ce comportement avec l’affichage ’D’, où on constate que la modification de la liste placée dans la variable locale x s’est répercutée sur la liste placée dans la variable globale a (c’est normal, les deux variables pointent sur la même adresse, il y a encore égalité physique).

On retiendra donc que quand ils sont transmis en tant qu’argument d’une fonction, les objets composites mutables (et en particulier les listes, mais c’est valable aussi pour les dictionnaires, qui seront abordés plus loin) sont transmis par “adresse” plutôt que par “valeur”.

Rappelons qu’on peut toujours faire une copie indépendante d’une liste a en écrivant l’instruction x = a[:]

Retour à l’accueil du cours Python/Numpy

Sommaire du chapitre 4

  1. La valeur None, et l’instruction return
  2. L’espace de noms global
  3. L’espace de noms local d’une fonction
  4. Remarques sur les espaces de noms emboîtés
  5. Paramètres positionnels ou nommés, valeurs par défaut
  6. Rattrapage des exceptions
  7. Fonctions lambda
  8. Documentation des fonctions