Portée des variables



La portée d'une variable désigne les parties du code source où elle est utilisable. Ce peut être par exemple, le projet entier (plusieurs fichiers source), le fichier source dans lequel la variable est crée, ou simplement une partie spécifique de ce fichier.

Variable globales et locales

En Python, la portée d'une variable peut être:

Dans un fichier source ne contenant pas de sous-programme toutes les variables sont donc globales. Lorsqu'il contient des sous-programmes, elles peuvent être locales ou globales.

La règle permettant de savoir si une variable est locale ou globale est implicite:

Toutes les variables subissant une affectation dans le corps d'un sous-programme sont par défaut locales à ce sous-programme et ne peuvent donc être utilisées en dehors de ce sous-programme. Par contre, les variables apparaissant dans le membre droit d'une affectation sont par défaut globales (à moins qu'elles n'aient auparavant subit une affectation dans le même sous-programme).

Cela signifie donc qu'un sous-programme ne peut pas modifier une variable externe à ce sous-programme.

Nous verrons ultérieurement, qu'il existe toutefois une manière d'échapper à cette règle implicite.

Exemple
def Somme () :
    x = y  +  z
 

Dans ce sous-programme x est une variable locale, alors que y et z sont globales. Il est donc totalement inutile, puisque le résultat du calcul contenu dans x ne peut pas être retransmis à l'extérieur !

Utilité des variables locales

La notion de variable locale a été introduite en programmation dans le but d'augmenter la modularité des programmes. En effet, avec les variables locales, il devient possible de réutiliser le même nom de variable dans différents sous-programmes sans introduire aucune dépendance entre ces sous-programmes. On augmente ainsi la facilité de mise à jour et on évite également une erreur de programmation fréquente appelée effet de bord.

Pour illustrer la différence entre une variable locale et une variable globale, nous commencerons par quelques exemples de programmes. Un de ces exemples servira à expliquer la notion d'effet de bord.

Ces différents exemples de programmes se trouvent dans le répertoire Exemple-Python-Sous-Programme. Vous pouvez donc les ouvrir et les exécuter pour vous aider à mieux comprendre.

Exemple 1

Cet exemple ce trouve dans le fichier:

 Exemple-Python-Sous-Programme/Local0.py

La boucle de saisie des données affiche ceci:

Local0.jpg, 8,8kB

Et voici le code du programme:

def Affecter_1961_a_X () :
    x = 1961

def Afficher_X () :
    print("X = "+ str(x))

# Point d'entree
choix = 0

while choix !=3 :
    print("Affecter 1961 a X : tapez 1")
    print("Afficher X : tapez 2")
    print("Arreter : tapez 3")

    choix = int(input("Votre choix : "))

    if choix == 1 :
        Affecter_1961_a_X () 	  
    elif choix == 2 :
        Afficher_X()

En principe, on s'attendrait à ce que le programme affiche X = 1961 lorsque l'utilisateur effectue le choix 1, puis le choix 2. Or, il n'en est rien !

On obtient un message d'erreur lors de l'exécution du choix 2. L'erreur est localisée dans l'instruction print située dans le sous-programme Afficher_X :

Local0-Erreur-ZoneEdition.jpg, 9,1kB

Le message d'erreur apparaissant dans l' onglet exception est name 'x' is not defined, ce qui signifie en francais : le nom 'x' n'est pas défini.

Local0-Erreur-Ongletexception.jpg, 38kB

Comment expliquer cette erreur ?

Le problème vient de l'affectation x = 1961 dans le sous-programme Affecter_1961_a_X: elle créé une variable locale à ce sous-programme. Autrement dit, lorsque le sous-programme a fini de s'exécuter, la variable x n'existe plus !

Il est donc impossible d'afficher la valeur de x dans le sous-programme Afficher_X.

Exemple 2

En fait, ce que nous avons essayé de faire dans le programme précédent, ne peut être réalisé qu'avec une variable globale. Nous allons voir dans ce deuxième exemple, qu'il est possible de créer une variable globale à partir d'un sous-programme.

Cet exemple ce trouve dans le fichier:

 Exemple-Python-Sous-Programme/Global.py

et voici un extrait du code de ce programme (la partie en pointillée est identique à celle du programme Local0):

def Affecter_1961_a_X () :
    global x
    x = 1961


def Afficher_X () :
    print("X = " + str(x))

    ....

C'est l'utilisation de l'instruction global qui règle le problème. Cette instruction indique à l'interprèteur Python que la variable qui suit (x ici) doit être gèrée comme une variable globale. Nous dirons que x est déclarée comme une variable globale.

De manière générale une déclaration de variables globales s'écrit:

 global  liste de noms de variable

S'il y a plusieurs noms de variable, ils doivent être séparés par des virgules.

Lorsqu'une variable est déclarée en global elle est traitée comme une variable 'normale'. Plus précisement, toute affectation à cette variable qui suit la déclaration va soit créer cette variable en tant que variable globale (s'il elle n'existe pas déjà) soit modifier sa valeur dans le cas contraire.

Dans notre exemple, l'affectation x = 1961 crée donc cette fois-ci une variable globale qui subsiste après l'exécution du sous-programme Affecter_1961_a_X, ce qui permet de l'afficher dans le sous-programme Afficher_X.

Exemple 3

Cet exemple ce trouve dans le fichier:

 Exemple-Python-Sous-Programme/Local1.py

Il s'agit d'une version légèrement modifiée du programme précédent, dans laquelle le nom x désigne à la fois une variable globale et une variable locale.

def Affecter_1961_a_X () :
    x = 1961


def Afficher_X () :
    print("X = "+ str(x))

# Point d'entree
choix = 0
x = 1935
while choix !=3 :
.....
    

Dans cette nouvelle version, la procédure Affecter_1961_a_X crée x en tant que variable locale. D'autre part, l'affectation x = 1935 en dehors de tout sous-programme crée une variable globale de même nom.

On constate que si l'on exécute à présent le choix 1, puis le choix 2, le programme affiche X = 1935:

Local1.jpg, 21kB

L'exécution du choix 1 est donc totalement sans effet. Essayons de comprendre pourquoi.

La première affectation à une variable nommée x, est l'affectation x = 1935 située juste après le point d'entrée. Elle a pour effet de créer une variable globale x de valeur 1935.

Comme cette affectation est située avant la boucle de saisie des données, cette variable existe avant même que l'utilisateur ait effectué un choix quelconque.

Lorsque l'utilisateur effectue le choix 1, une variable locale également nommée x est créé par le sous-programme Affecter_1961_a_X. Mais cette création de variable est totalement inutile puisque cette variable cesse d'exister à la sortie du sous-programme.

Lorsqu'il effectue le choix 2, c'est le sous-programme Afficher_X qui s'exécute. Le corps de ce sous-programme contient une instruction affichant une variable nommée x. Il s'agit donc forcément ici de la variable globale x qui a pour valeur 1935.

Retour sur le programme peinture

Revenons sur le programme Peinture et plus particulièrement sur la déclaration de sous-programmes.

Nous pouvons à présent expliquer l'intérêt des instructions global dans chaque sous-programme: sans ces instructions le programme ne fonctionnerait pas puisque les résultats intermédiaires (SurfaceDesMurs, SurfaceDesFenetres, SurfaceDesPortes) ne pourraient pas être communiqués au sous-programme CalculerSurface_A_Peindre.

De même, l'instruction global SurfacePeinture dans le sous-programme CalculerSurface_A_Peindre est indispensable pour pouvoir ensuite afficher la surface à peindre dans l'instruction d'affichage du résultat du programme.

Notez également que les variables apparaissant dans les membres droits des affectations n'ont pas besoin d'être déclarées en global. Par exemple, dans CalculerSurfaceDesMurs, on a l'affectation:

 SurfaceDesMurs =  \
    2 * (LargeurPiece * HAUTEUR_MUR + \
         LongueurPiece * HAUTEUR_MUR)

Les variables LargeurPiece, HAUTEUR_MUR, LongueurPiece, HAUTEUR_MUR sont par défaut globales. Seule la variable affectée (SurfaceDesMurs) doit être déclarée en global, sans quoi une variable locale de même nom serait créée et le sous-programme serait sans effet.

L'effet de bord

Pour terminer notre série d'exemples, voici un programme contenant un effet de bord (fichier source : Effet-De-Bord.py) :

 def Ligne () :
    global i,L
    i = 1
    while i <= L :
        print("*",end="")
        i+=1
    print()
    
def Rectangle () :
    global i,H
    i = 1
    while i <= H :
        Ligne()
        i+=1

L = int(input("Largeur : "))
H = int(input("Hauteur : "))
Rectangle()

Ce programme est censé afficher un rectangle de dimensions données composé d'étoiles.

En l'exécutant, vous constaterez qu'il ne fonctionne pas. Lorsque la hauteur est inférieur ou égale à la largeur on obtient une seule ligne et sinon une boucle infinie !

Voici par exemple, le résultat avec une largeur de 5 et une hauteur égale à 3:

EffetBord5X3.jpg, 3,8kB

Le problème vient du fait que la variable i (compteur de boucle) est déclarée en global dans les deux sous-programmes.

A priori, i a deux fonctions :

Mais comme i est déclaré en global, le premier comptage influence le deuxième. En effet, au retour de l'appel de Ligne dans le sous-programme Rectangle, i ne contient plus le nombre de lignes affichés par ce sous-programme, mais le nombre d'étoiles affichées dans le dernier appel du sous-programme Ligne! Nous avons un effet de bord.

Les variables locales servent (entre autre) à éviter ce genre de problème.

Dans notre exemple, il suffit de ne pas déclarer i en global pour règler le problème:

 def Ligne () :
    global L
    i = 1
    while i <= L :
        print("*",end="")
        i+=1
    print()
    
def Rectangle () :
    global H
    i = 1
    while i <= H :
        Ligne()
        i+=1

L = int(input("Largeur : "))
H = int(input("Hauteur : "))
Rectangle()

Comme i est cette fois-ci une variable locale, il n'y a plus aucune interaction entre le comptage d'étoiles et le comptage de lignes.

Si vous souhaitez le vérifier par vous même, vous pouvez exécuter le programme corrigé. Il se trouve dans le fichier Sans-Effet-De-Bord.py.

Principe généraux

Pour terminer cette partie du cours résumons les règles principales définissant la portée des variables en Python.

Une variable peut être:

Pour les variables globales, il y faut distinguer deux types de portées: