Aller au contenu

Variables locales, variables globales

Exercice

Lisez le code dans l'éditeur puis exécutez-le :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Explication

Les variables a et b ont été définies et la fonction a bien été appelée, où est le problème ?

En fait, les variables définies à l'intérieur d'une fonction n'existent plus dès que le programme a fini d'exécuter cette fonction.

Ici, les variables a et b sont créées dans la fonction mais n'existent pas en dehors de la fonction, d'où l'erreur NameError très explicite.

Observez ce qu'il se passe ici (cliquez sur Next et observez qu'à l'étape 7, les variables a et b disparaissent) :

À retenir

Des variables définies à l'intérieur d'une fonction sont dites locales.

Les variables définies à l’extérieur d’une fonction sont des variables globales. Leur contenu est « visible » de l’intérieur d’une fonction, mais la fonction ne peut pas le modifier.

# ici les variables sont globales

def ma_fonction(param1, param2, ...):
    # ici les variables sont locales
    # en particulier param1, param2, ... sont des variables locales

# ici les variables sont globales

Exercice

Lisez le code dans l'éditeur et prenez le temps de deviner les valeurs de a, b et c.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Exécutez le script pour vérifier vos réponses.

Explications

Observons ce qu'il se passe pour chacune des variables :

  • la variable a est définie à l'extérieur de la fonction (et avant l'appel de la fonction) donc elle est globale et son contenu est connu dans la fonction (par contre la fonction ne pourrait pas modifier sa valeur) ;
  • la variable b : du fait de l'instruction b = 5, il y a création d'une variable locale b, dont la valeur est perdue à la sortie de la fonction ;
  • avant de se terminer, la fonction renvoie a * b donc 4 fois 5 car la variable locale b vaut 5 ;
  • la variable c est définie à l'extérieur de la fonction donc elle est globale et son contenu est défini par l'affectation c = fonction_bidon().

Ici, la variable b utilisée dans la fonction n'est donc pas la même que celle qui était définie en dehors de cette fonction !

Observez cela à l'étape 7 :

Pour éviter la confusion, il serait peut-être plus pertinent de donner un autre nom à la variable b dans la fonction :

def fonction_bidon():
    d = 5
    return a * d

a = 4
b = 3
c = fonction_bidon()

mais d'un point de vue des valeurs finales de a, b et c cela ne changera rien.

Exercice

Testez le script :

def fonction_bidon():
    b = 7
    print(b)

b = 3
fonction_bidon()

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

puis échangez les deux instructions de la fonction...

Une erreur très subtile

Il faut savoir que lors des affectations dans une fonction, des variables locales sont créées. Voyons cela en modifiant le script précédent.

Script 1

def fonction_bidon():
    print(b)

b = 3
fonction_bidon()

la variable b est globale et sa valeur est connue dans la fonction, celle-ci affiche donc 3, logique.

Script 2

def fonction_bidon():
    b = 7
    print(b)

b = 3
fonction_bidon()

Ici, il y a une affectation (b = 7) donc une variable locale b a été créée et le print est dans la fonction donc c'est la valeur locale de b qui est affichée.

Script 3

def fonction_bidon():
    print(b)
    b = 7
    print(b)

b = 3
fonction_bidon()

Logiquement, la fonction devrait afficher 3 puis 7. Mais au lieu de cela il y a une erreur signalée à la ligne print(b) alors que la même ligne fonctionnait dans le script 2 !

En fait, l'erreur est causée par la ligne suivante (b = 7)... En quelque sorte Python lit deux fois la fonction :

  • la première fois, Python n'exécute pas les instructions mais, du fait de l'affectation b = 7, une variable locale b est créée ;
  • la deuxième fois, Python exécute la première instruction print(b) mais la variable locale b n'a pas de valeur à ce moment-là d'où l'erreur.

Un conseil donc : si vous voulez utiliser des valeurs de variables globales à l'intérieur d'une fonction, passez-les en paramètres :

def fonction_bidon(b):
    print(b)
    b = 7
    print(b)

b = 3
fonction_bidon(b)

Ici b est un paramètre donc une variable locale b est créée dès le début de la fonction et prend la valeur 3 au moment de l'appel de la fonction.

Et, pour plus de clarté, il vaut mieux éviter de modifier les valeurs passées en paramètres :

def fonction_bidon(param):
    print(param)
    resultat = 7
    print(resultat)

Exercice sur le vocabulaire

Associez les éléments du programme avec les termes de vocabulaire.

Exercice

Sans utiliser Python, analysez ce script puis donnez les valeurs finales de a, b et c :

def fonc(a, b):
    a = a + 1
    b = b - 2
    c = a + b
    return c

a = 2
b = 1
fonc(a, b)

Quand vous aurez fini, vérifiez avec l'éditeur (avec des print) ou avec Python Tutor.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Exercice

Même consigne :

def une_fonction(e):
    f = 3
    k = f * e - c
    return k

a = 4
c = 5
b = une_fonction(a)

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier