Aller au contenu

13 Cours Portée Variables

Espace de nommage et portée des variables⚓︎

Exemple 1⚓︎

Observer l’exemple ci-dessous.

🐍 Script Python
def func1():
    a = 1
    print(f"Dans func1(), a = {a}")

def func2():
    print(f"Dans func2(), a = {a}")

a = 2
func1()
func2()
print(f"Dans l'espace englobant, a = {a}")
📋 Texte
Dans func1(), a = 1
Dans func2(), a = 2
Dans l'espace englobant, a = 2

Cet exemple montre deux comportements :

  1. Une variable définie localement à l’intérieur d’une fonction cache une variable du même nom définie dans l’espace englobant (cas de func1()).
  2. Quand une variable n’est pas définie localement à l’intérieur d’une fonction, Python va chercher sa valeur dans l’espace englobant (cas de func2()).

Exemple 2⚓︎

On veut illustrer le mécanisme de portée des variables au sein des fonctions :

🐍 Script Python
def func():
    a = 1
    bbb = 2
    print(f"Dans  func() : a = {a}")

a = 2
print(f"Avant func() : a = {a}")
func()
print(f"Après func() : a = {a}")
📋 Texte
Avant func() : a = 2
Dans  func() : a = 1
Après func() : a = 2

Illustration avec le site python tutor.

Les variables définies localement à l’intérieur d’une fonction sont détruites à la sortie de cette fonction. Ici, la variable bbb n’existe pas hors de la fonction func(), donc Python renvoie une erreur si on essaye d’utiliser bbb depuis l’espace englobant :

🐍 Script Python
print(bbb)
📋 Texte
Traceback (most recent call last):
  File "...", line 10, in <module>
    print(bbb)
NameError: name 'bbb' is not defined

Variable locale⚓︎

Voici une fonction toute simple qui prend en entrée un nombre et renvoie le nombre augmenté de 1.

🐍 Script Python
def ma_fonction(x):
    x = x + 1
    return x
  • Bien évidemment ma_fonction(3) renvoie 4.
  • Voici un exemple d'utilisation de la fonction :
    🐍 Script Python
    y = 5
    print(ma_fonction(y))
    print(y)
    
    ma_fonction(y) renvoie 6.
    La valeur de y n’a pas changé, elle vaut toujours 5.
  • Voici la situation problématique qu’il faut bien comprendre :
    🐍 Script Python
    x = 7
    print(ma_fonction(x))
    print(x)
    
    • La variable x est initialisée à 7.
    • L’appel de la fonction ma_fonction(x) est donc la même chose que ma_fonction(7) et renvoie logiquement 8.
    • Que vaut la variable x à la fin ? La variable x est inchangée et vaut toujours 7! Même s’il y a eu entre temps une instruction x = x + 1. Cette instruction a changé le x à l’intérieur de la fonction, mais pas le x en dehors de la fonction.
    • pour l'ordinateur, cette configuration est la même que la précédente. Dans la précédente, la variable y et la variable xétaient bien distinctes. L'instruction x = x + 1 ne modifie pas y. Dans ce deuxième exemple, la variable x à l'intérieur de la fonction est bien distincte de la variable x de l'espace englobant.

Ce qu'il faut retenir :

  • Les variables définies à l’intérieur d’une fonction sont appelées variables locales. Elles n’existent pas en dehors de la fonction.
  • Si une variable dans une fonction porte le même nom qu’une variable dans le programme (comme le x dans l’exemple ci-dessus), il y a deux variables distinctes ; la variable locale n’existant que dans la fonction. Pour bien comprendre la portée des variables, on peut colorier les variables globales d’une fonction en rouge, et les variables locales avec une couleur par fonction. Le petit programme suivant définit une fonction qui ajoute un, et une autre qui calcule le double.

Le programme affiche d’abord la valeur de x, donc 7, puis il ajoute un à 7, il affiche donc 8, puis il affiche le double de x, donc 14. La variable globale x n’a jamais changé, le dernier affichage de x est donc encore 7.

Variable globale⚓︎

Une variable globale est une variable qui est définie pour l’ensemble du programme. Il n’est généralement pas recommandé d’utiliser de telles variables, mais cela peut être utile dans certain cas. Voyons un exemple. On déclare la variable globale, ici la constante de gravitation, en début de programme comme une variable classique :

🐍 Script Python
gravitation = 9.81

La contenu de la variable gravitation est maintenant accessible partout. Par contre, si on souhaite changer la valeur de cette variable dans une fonction, il faut bien préciser à Python que l’on est conscient de modifier une variable globale ! Par exemple pour des calculs sur la Lune, il faut changer la constante de gravitation qui y est beaucoup plus faible.

🐍 Script Python
def sur_la_lune():
    global gravitation # Oui, je veux modifier cette variable globale!
    gravitation = 1.625 # Nouvelle valeur pour tout le programme
...

Nota Bene : en fait, les conventions de nommage précisent que, pour une variable globale utilisée comme constante, on utilise plutôt des majuscules.
Ce serait donc GRAVITATION...

Attention aux mutables⚓︎

Ce qui précède est 100% applicable à toutes les variables immutables :

🐍 Script Python
def saisie_nom_du_joueur():
    nom = input(f"Merci de saisir le nom du joueur : ")

nom = "personne"
print(nom)
saisie_nom_du_joueur()
print(nom)
📋 Texte
personne
Merci de saisir le nom du joueur : toto
personne

En revanche, il faut savoir que les variables mutables ont un comportement un peu "global".
Il faut s'en méfier, car cela peut faire modifier des listes sans y préter attention...
Donc éviter ce type d'usage :

🐍 Script Python
def saisie_noms_des_joueurs():
    for i in range(1,4):
        nom = input(f"Merci de saisir le nom du joueur n°{i} : ")
        liste_joueurs.append(nom)

liste_joueurs = []
print(liste_joueurs)
saisie_noms_des_joueurs()
print(liste_joueurs)

📋 Texte
[]
Merci de saisir le nom du joueur n°1 : riri
Merci de saisir le nom du joueur n°2 : fifi
Merci de saisir le nom du joueur n°3 : loulou
['riri', 'fifi', 'loulou']

Mais en revanche :

🐍 Script Python
def saisie_noms_des_joueurs():
    liste_joueurs = []
    for i in range(1,4):
        nom = input(f"Merci de saisir le nom du joueur n°{i} : ")
        liste_joueurs.append(nom)

liste_joueurs = []
print(liste_joueurs)
saisie_noms_des_joueurs()
print(liste_joueurs)

📋 Texte
[]
Merci de saisir le nom du joueur n°1 : riri
Merci de saisir le nom du joueur n°2 : fifi
Merci de saisir le nom du joueur n°3 : loulou
[]

Comment volontairement récupérer une variable modifiée dans une fonction ?⚓︎

A priori, pour récupérer les valeurs de variables modifiées dans une fonction, on les retourne.

🐍 Script Python
def saisie_nom_du_joueur():
    nom = input(f"Merci de saisir le nom du joueur : ")
    return nom

nom = "personne"
print(nom)
nom = saisie_nom_du_joueur()
print(nom)
📋 Texte
personne
Merci de saisir le nom du joueur : toto
toto
🐍 Script Python
def saisie_noms_des_joueurs():
    liste_joueurs = []
    for i in range(1,4):
        nom = input(f"Merci de saisir le nom du joueur n°{i} : ")
        liste_joueurs.append(nom)
    return liste_joueurs

# et dans le programme principal, on récupère ce que retourne cette fonction :
liste_joueurs = saisie_noms_des_joueurs()
print(liste_joueurs)
📋 Texte
Merci de saisir le nom du joueur n°1 : riri
Merci de saisir le nom du joueur n°2 : fifi
Merci de saisir le nom du joueur n°3 : loulou
['riri', 'fifi', 'loulou']
🐍 Script Python
# la variable peut ou non s'appeler également liste_joueurs :
noms_joueurs = saisie_noms_des_joueurs()
print(noms_joueurs)
📋 Texte
Merci de saisir le nom du joueur n°1 : riri
Merci de saisir le nom du joueur n°2 : fifi
Merci de saisir le nom du joueur n°3 : loulou
['riri', 'fifi', 'loulou']

et les variables en paramètres ?⚓︎

Attention ! Les variables en paramètres se comportent différemment suivant si elles sont mutables ou immutables :

  • les immutables ne sont pas modifiées, seules leurs valeurs sont passées dans la fonction :
    avec des nombres :
    🐍 Script Python
    def modif_nombre(x):
        x = x + 1
        return x
    
    y = 3
    z = modif_nombre(y)
    print(str(y) + " (y n'est pas modifié)")
    print(str(z) + " (z a bien récupéré le retour de la fonction)")
    
📋 Texte
3 (y n'est pas modifié)
4 (z a bien récupéré le retour de la fonction)

avec des chaînes de caractères :

🐍 Script Python
def modif_texte(texte):
    texte = texte + ", bonsoir"
    return texte

texte2 = "bonjour"
texte3 = modif_texte(texte2)
print(texte2 + " (texte2 n'est pas modifiée)")
print(texte3 + " (texte3 a bien récupéré le retour de la fonction)")

📋 Texte
bonjour (texte2 n'est pas modifiée)
bonjour, bonsoir (texte3 a bien récupéré le retour de la fonction)
  • les mutables passent par adresse donc le paramètre local a la même adresse mémoire que l'argument passé, et si on modifie le paramètre local, on modifie en fait l'argument :
    avec des listes si on profite du caractère mutable de la liste et qu'on la modifie :
    🐍 Script Python
    def modif_liste_tout(liste):
        liste.append(8)
        return liste
    
    liste1 = [2, -3, 4, -5, 6, -7]
    liste2 = modif_liste_tout(liste1)
    print(liste1, " (!!! liste1 est modifiée !!!")
    print(liste2, " (liste2 a bien récupéré le retour de la fonction)")
    
📋 Texte
[2, -3, 4, -5, 6, -7, 8]  (!!! liste1 est modifiée !!!
[2, -3, 4, -5, 6, -7, 8]  (liste2 a bien récupéré le retour de la fonction)
  • Néanmoins, si on ne fait pas de modification, mais qu'on fait une réaffectation, une nouvelle adresse sera affectée au paramètre, sans modification de la variable globale :
    🐍 Script Python
    def modif_liste_tout(liste):
        liste = [x for x in liste if x > 0]
        return liste
    
    liste1 = [2, -3, 4, -5, 6, -7]
    liste2 = modif_liste_tout(liste1)
    print(liste1, " (liste1 n'est pas modifiée)")
    print(liste2, " (liste2 a bien récupéré le retour de la fonction)")
    
📋 Texte
[2, -3, 4, -5, 6, -7]  (liste1 n'est pas modifiée)
[2, 4, 6]  (liste2 a bien récupéré le retour de la fonction)

Dans tous les cas, le paramètre de la fonction n'existe pas en dehors de la fonction.
Si on ajoute print(liste) à la fin de l'un de ces exemples, on obtient toujours :

📋 Texte
NameError: name 'liste' is not defined