Variables locales d'une fonction☘
Portée locale☘
Considérons le code suivant:
1 2 3 4 5 6 7 | def f(x): b = 2 return b*x a = f(5) print(a) print(b) |
On obtient d'abord l'affichage de la valeur de a
(c'est à dire 10) puis une erreur:
1 2 3 4 5 6 7 8 9 10 | 10 --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-2-92f68300172b> in <module> 5 a = f(5) 6 print(a) ----> 7 print(b) NameError: name 'b' is not defined |
L'erreur est due au fait que nous avons cherché à accèder à b dans une zone du programme dans laquelle b n'existe pas.
L' affectation b = 2
dans la fonction définit une étiquette sur l'objet 2 (de type int) mais ce lien
entre l'objet et l'étiquette n'a d'existence que localement:
dès que l'on sort de la fonction, l'étiquette b n'existe plus
et, en sortie de la fonction, on n'a plus d'accès à l'objet qui était ainsi étiqueté
(sauf si on le renvoie avec return).
Schéma:
On a entouré b afin d'illustrer une certaine "étanchéité" entre les deux espaces (l'espace global et l'espace local à f).
Important
Toute affectation dans une fonction crée une étiquette sur un nouvel objet.
Le lien entre l'étiquette et l'objet n'existe qu'à l'intérieur de la fonction.
Un appel à cette variable en dehors de la fonction provoque donc une erreur.
Variable créée extérieurement☘
Considérons le code suivant:
1 2 3 4 5 6 7 8 | def f(x): b = 2 print(c) return b*x c = 42 a = f(5) |
Qu'obtient-on?
solution
On obtient:
1 | 42 |
Ainsi la variable c définie à l'extérieur de f est accessible.
Variable créée extérieurement et variable locale☘
Avec le code suivant, qu'obtient-on? Chercher à interpréter.
1 2 3 4 5 6 7 8 | def f(): c = 666 print(c) c = 42 f() print(c) |
Une réponse
On obtient:
1 2 | 666 42 |
Si l'on suit l'ordre du code:
- les lignes 1 à 3 définissent f ("affectation" de lignes d'instructions au nom f)
- En ligne 6, on définit c (affectation de la valeur 42)
- En ligne 7, on demande l'exécution de f: une variable locale à f de nom c est alors créée (ligne 2). Cette variable masque alors le c de la partie externe à f, seul le c local à f est accessible. L'affichage demandé en ligne 3 donne donc 666. Puis f se termine, l'étiquette locale c disparaît.
- On exécute maintenant la ligne 8. Le c local de la fonction ayant été oublié, le seul c accessible est celui défini en ligne 6. L'affichage donne donc 42.
Important
Si une affectation a = valeur d'un objet
a lieu dans une fonction f, l'étiquette a masque le temps
d'exécution de la fonction f toute étiquette de nom a externe à f.
Les opérations ayant lieu dans f auront donc lieu sur cette variable locale et non sur une variable externe.
Remarque
Les variables locales d'une fonction sont stockées dans un dictionnaire.
On peut observer ici les variables:
1 2 3 4 5 6 7 8 9 10 11 12 13 | def f(): c = 666 a = 777 print("f : locals:", locals()) c = 42 d = 1515 f() print() print("Variables dans le champ principal: ", [(n,v) for (n,v) in locals().items() if not n.startswith('_')]) print() |
On obtient:
1 2 3 | f : locals: {'c': 666, 'a': 777} Variables dans le champ principal: [('f', <function f at 0x7f21f5a9d1e0>), ('c', 42), ('d', 1515)] |
On voit que dans f, l'étiquette c cible un entier de valeur 666, tandis que dans la partie extérieure à f, c cible un entier de valeur 42. (Au passage, on voit que f est une variable au même titre que les variables entières)
Variable créée extérieurement et variable locale, bis☘
Et avec le code suivant, qu'obtiendra-t-on?
1 2 3 4 5 6 7 8 9 | def f(x): b = 2 print(c) c = 666 return b*x c = 42 a = f(5) |
Donner une explication.
Réponse
On obtient:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) <ipython-input-4-d4286a67556f> in <module> 7 8 c = 42 ----> 9 a = f(5) <ipython-input-4-d4286a67556f> in f(x) 1 def f(x): 2 b = 2 ----> 3 print(c) 4 c = 666 5 return b*x UnboundLocalError: local variable 'c' referenced before assignment |
Nous n'avons donc plus accès à la variable c externe à la fonction.
Explication:
Comme nous l'avons vu, l'affectation c = 666
crée une variable locale à la fonction.
L'appel à print
étant fait avant l'affectation c = 666
, on pourrait s'attendre
qu'à ce stade, c puisse faire référence à la variable externe
puis que c devienne à la ligne suivante une étiquette "locale".
Toutefois, nous constatons et retiendrons qu'à l'intérieur de la fonction, il ne peut y avoir coexistence d'une variable interne et d'une variable externe de même nom. Si ce n'était pas le cas, un code qui profiterait d'une telle possibilité serait d'ailleurs un code très peu lisible, obligeant le lecteur à une gymnastique inutile pour savoir "qui est qui".
Note
La possibilité de donner un même nom à des variables dans des fonctions différentes sans que celles-ci n'interfèrent est importante. Elle permet par exemple de nommer régulièrement i les variables de boucle, ou plus généralement de faire des choix "réguliers" pour faciliter la lecture du code.
A contrario, il peut être utile d'éviter dans les fonctions des noms d'objets "importants" du programme pour un usage local et éphémère afin de faciliter la lecture... A décider dans chaque cas particulier, la lisibilité (compréhension, maintenance du code...) devant être priviligiée dans la plupart des situations.