TP - Variable locale, variable globale☘
De manière générale, les variables définies à l'intérieur des fonctions sont locales. Elles n'existent pas en dehors de cette fonction et ne modifient pas le contenu des autres variables du programme, extérieures à la fonction, même si celles-ci ont la même étiquette (le même « nom »).
Ce TP guidé a pour objectif de vous permettre de faire la différence entre ces différentes définitions afin d'anticiper au mieux le comportement d'un programme.
Exemple 1 - En dehors et dans une fonction☘
-
Anticipez les affichages réalisés par le code ci-dessous et notez-les sur votre cahier.
1 2 3 4 5 6 7 8 9 10
a = 2 def f(): a = 5 print("A l'intérieur de f, a =", a) print("Avant d'appeler la fonction f, a =", a) f() print("Après avoir appelé la fonction f, a =", a)
-
Vérifiez votre anticipation en copiant/collant/exécutant ce code.
Affichages obtenus
Avant d'appeler la fonction f, a = 2 A l'intérieur de f, a = 5 Après avoir appelé la fonction f, a = 2
Vous pouvez aussi visualiser ce comportement sur le site Python tutor.
-
Essayez d'expliquer pourquoi ces affichages ont eu lieu avant de valider votre raisonnement avec les explications ci-dessous.
Explications des lignes 1 à 8
Le
a
de la ligne 1 est dans l'espace global : il est connu en n'importe quel point du programme.- Après la ligne 1, la situation peut être schématisée ainsi :
- Les lignes 3 à 5 définissent la fonction
f()
et ne sont pas exécutées tant quef()
n'est pas appelée. - La ligne 8 est exécutée et affiche la valeur de l'objet portant le
nom
a
: cette ligne affiche donc2
(ou plutôt : «Avant d'appeler la fonction f, a = 2
»).
Explications des lignes 9 et 10
- En ligne 9, on fait un appel à
f()
. L'appelf()
a pour effet de créer une étiquettea
locale àf()
avec l'affectationa = 5
. On pourrait en fait noter cette variablea_f
pour la distinguer de l'étiquettea
de portée globale. - L'exécution de la ligne 5 (corps de
f()
) affiche la valeur de l'objet étiquetéa
local à la fonctionf()
.
La fonction se termine : son espace de noms disparaît. - La ligne 10 affiche la valeur du seul objet de l'espace global
étiqueté
a
.
- Après la ligne 1, la situation peut être schématisée ainsi :
Exemple 2 - En dehors d'une fonction☘
-
Anticipez les affichages réalisés par le code ci-dessous et notez-les sur votre cahier.
1 2 3 4 5 6 7 8 9 10
a = 2 def f(x): print("A l'intérieur de f, a =", a) return x+2 print("Avant d'appeler la fonction f, a =", a) f(a) print("Après avoir appelé la fonction f, a =", a)
-
Vérifiez votre anticipation en copiant/collant/exécutant ce code.
Affichages obtenus
Avant d'appeler la fonction f, a = 2 A l'intérieur de f, a = 2 Après avoir appelé la fonction f, a = 2
Vous pouvez aussi visualiser ce comportement sur le site Python tutor.
-
Essayez d'expliquer pourquoi ces affichages ont eu lieu avant de valider votre raisonnement avec les explications ci-dessous.
Explications des lignes 1 à 8
Le
a
de la ligne 1 est dans l'espace global : il est connu en n'importe quel point du programme.- Après la ligne 1, la situation peut être schématisée ainsi :
- Les lignes 3 à 5 définissent la fonction
f()
et ne sont pas exécutées tant quef()
n'est pas appelée. - La ligne 8 est exécutée et affiche la valeur de l'objet portant le
nom
a
: cette ligne affiche donc2
(ou plutôt : «Avant d'appeler la fonction f, a = 2
»).
Explications des lignes 9 et 10
- En ligne 9, on fait un appel à
f()
. L'appelf(a)
a pour effet de créer une étiquettex
locale àf()
avec l'affectation (d'étiquette)x = a
:
- L'exécution de la ligne 4 (corps de
f()
) affiche la valeur du seul objet étiquetéa
.
Remarque : l'étiquette est aussi connue def()
car l'étiquettea
est dans l'espace global. - La ligne 5 «
return x+2
» crée un objet de typeint
et de valeur4
(mais sans étiquette) :
La fonction se termine : son espace de noms disparaît. - La ligne 10 affiche la valeur du seul objet de l'espace global
étiqueté
a
.
- Après la ligne 1, la situation peut être schématisée ainsi :
Exemple 3 - Variable locale et variable globale de même nom☘
-
Anticipez les affichages réalisés par le code ci-dessous et notez-les sur votre cahier.
1 2 3 4 5 6 7 8 9 10
a = 2 def f(x): a = x + 3 return a print("Avant d'appeler la fonction f, a =", a) f(a) print("Après avoir appelé la fonction f, a =", a)
-
Vérifiez votre anticipation en copiant/collant/exécutant ce code.
Affichages obtenus
Avant d'appeler la fonction f, a = 2 Après avoir appelé la fonction f, a = 2
Vous pouvez aussi visualiser ce comportement sur le site Python tutor.
-
Essayez d'expliquer pourquoi ces affichages ont eu lieu avant de valider votre raisonnement avec les explications ci-dessous.
Explications des lignes 1 à 8
Le
a
de la ligne 1 est dans l'espace global : il est connu en n'importe quel point du programme.- A la ligne 1, on crée une étiquette
a
de portée globale sur un objet de typeint
:
- La ligne 8 est exécutée et affiche la valeur de l'objet portant le
nom
a
.
Explications des lignes 9 et 10
-
Ligne 9 : on fait un appel à la fonction
f()
parf(a)
: -
Ligne 4 (corps de de la fonction
f()
) : on créé une étiquettea
locale àf()
. On pourrait la notera_f
pour la distinguer de l'étiquettea
de portée globale. - En ligne 5 (corps de de la fonction
f()
), l'instruction «return a
» permet de conclure que l'image de2
parf()
est5
. Cependant aucune étiquette au niveau global n'est affectée à cette valeur, si bien qu'en sortie def()
on n'a plus accès à cette image car les noms locaux àf()
disparaissant ! - La ligne 10 affiche la valeur du seul objet de l'espace global
étiqueté
a
.
Remarque importante
A l'intérieur de la fonction, l'étiquette
a
désigne ainsi un autre objet qu'à l'extérieur de la fonction (elle masque la variablea
de l'espace global).
Les opérations faites alors sur cea
local n'affectent pas lea
global.Lorsqu'on sort de la fonction, le
a
local à la fonction disparaît (il n'existe que le temps d'exécution de la fonction) et c'est à nouveau lea
global de la ligne 1 que l'on manipule.En d'autres termes, le code de notre script pourrait être réécrit comme suit de façon tout à fait équivalente:
1 2 3 4 5 6 7 8 9
a = 2 def f(x): b = x + 3 return b print("Variable a avant appel de f:", a) f(a) print("Variable a après appel de f:", a)
- A la ligne 1, on crée une étiquette
Exemple 4 - Variable globale et paramètre de même nom☘
-
Anticipez les affichages réalisés par le code ci-dessous et notez-les sur votre cahier.
1 2 3 4 5 6 7 8 9 10
a = 2 def f(a): a = a + 3 return a print("Avant d'appeler la fonction f, a =", a) f(a) print("Après avoir appelé la fonction f, a =", a)
-
Vérifiez votre anticipation en copiant/collant/exécutant ce code.
Affichages obtenus
Avant d'appeler la fonction f, a = 2 Après avoir appelé la fonction f, a = 2
Vous pouvez aussi visualiser ce comportement sur le site Python tutor.
-
Essayez d'expliquer pourquoi ces affichages ont eu lieu avant de valider votre raisonnement avec les explications ci-dessous.
Explications des lignes 1 à 8
- A la ligne 1, on crée un objet global étiqueté
a
de typeint
et de valeur2
:
- Les lignes 3 à 5 ne font que définir la fonction
f()
. Pour le moment, elles ne sont pas exécutées. - La ligne suivante à être exécutée est la ligne 8. Elle affiche la
valeur ciblée par
a
, c'est-à-dire2
.
Explications des lignes 9 et 10
Dans les deux cas, la valeur affichée est 2.
Décomposons:
- En ligne 9, l'appel est
f(a)
. Attention, à ce stade une nouvelle étiquettea
est créée, mais qui ne sera connue que de la fonctionf()
(on pourrait la nommera_f
pour la distinguer de l'étiquettea
globale déjà créée).
L'appelf(a)
commence par une affectationa_f ← a
, qui crée une nouvelle étiquette sur l'objeta
: - On exécute ensuite la ligne 4 (dans le corps de la fonction).
L'affectationa = a + 3
crée cette fois un nouvel objet qui recevra la valeur2+3
et l'étiquettea
. Attention, il s'agit là encore de l'étiquette localea
, c'est-à-dire dea_f
:
-
La ligne 5
return a
signifie quef(a)
vaut5
... mais on ne s'en sert pas dans ce code en renvoyant dans la partie globale !
La fonction se termine : tous les noms (étiquettes) locaux àf()
disparaissent :
-
On reprend le déroulé du programme à la ligne 10 et on affiche la valeur du seul objet encore étiqueté
a
: il a pour valeur2
.
Conséquence importante
Le code proposé peut donc être réécrit de façon parfaitement équivalente sous la forme :
1 2 3 4 5 6 7 8 9 10
a = 2 def f(b): b = b + 3 return b print("Avant d'appeler la fonction f, a =", a) f(a) print("Après avoir appelé la fonction f, a =", a)
Cette seconde écriture est certainement plus facile à lire car elle n'entraîne pas d'éventuelles confusions pour le lecteur entre des noms de variable de l'espace local à une fonction et des noms de variables de l'espace global.
Retenez cela :
En général, évitez toujours de donner le même nom à une variable globale et au paramètre d'une fonction. Cela ne fait que compliquer la lecture du code et rend ensuite difficile la recherche d'erreurs dans ce code... - A la ligne 1, on crée un objet global étiqueté
Exemple 5 - Variable globale seule☘
-
Anticipez les affichages réalisés par le code ci-dessous et notez-les sur votre cahier.
1 2 3 4 5 6 7 8 9 10
a = 2 def f(): a = a + 3 return a print("Avant d'appeler la fonction f, a =", a) f() print("Après avoir appelé la fonction f, a =", a)
-
Vérifiez votre anticipation en copiant/collant/exécutant ce code.
Affichages obtenus
On obtient une erreur :
line 4, in f a = a + 3 UnboundLocalError: local variable 'a' referenced before assignment
Vous pouvez aussi visualiser ce comportement sur le site Python tutor.
-
Essayez d'expliquer pourquoi ces affichages ont eu lieu avant de valider votre raisonnement avec les explications ci-dessous.
Explications
-
En ligne 1, on crée un objet global de type
int
et de valeur2
.
-
La ligne 9 fait un appel à
f()
. - On exécute ensuite la ligne 4 (dans le corps de la fonction).
L'affectationa = a + 3
est la ligne qui provoque une erreur. Pourquoi ?
Comme on a vu que le membre de droite d'une affectation était exécuté en premier lieu, on pourrait s'attendre à ce que le
a
de droite désigne lea
de l'espace global, puis qu'una = a_f
local à la fonctionf()
soit créé grâce à la partie gauche de l'instruction d'affectation.
Cela n'a pas lieu : la présence d'una
local et d'una
global dans un même espace de noms est interdit ! En effet, la lecture du code par la machine ne permet pas d'implicite. L'opération demandée ici est donc interdite.Même si la partie droite de l'instruction d'affectation est effectuée en premier, une lecture globale de l'instruction à quand même lieu. Cette lecture crée immédiatement un nom
a
local, ce qui fait que lea
de droite ne peut que désigner également cea
local.
Puisque cea
local n'a pas encore de valeur dans la partie droite, l'erreur est affichée :referenced before assignment
soit « variable référencée avant d'avoir été affectée ».Rappel
Nous avons vu dans les exemples précédents une façon qui permet de contourner ce problème.
Ainsi, les instructions suivantes ne renveront pas d'erreur (tout en étant inutile puisque, comme dans les exemples précédents, on n'utilise pas ici la valeur renvoyée par la fonctionf()
) :1 2 3 4 5 6 7 8 9
a = 2 def f(): b = a + 3 return b print("Avant d'appeler la fonction f, a =", a) f() print("Après avoir appelé la fonction f, a =", a)
La morale est toujours la même : évitez d'utiliser dans une fonction des noms qui peuvent prêter à confusion avec les noms de l'espace global, vous pourrez ainsi plus facilement lire et corriger votre code !
-