Python: Variables et espaces de noms

Pour une lecture plus agréable (page plus large), je vous invite à cliquer sur ce lien et à lire ce chapitre dans la rubrique consacrée au langage Python.

Introduction

Dans le chapitre précédent, nous avons pu constater que grâce à la programmation orientée objet, deux développeurs, Briface et Jobriel, pouvaient coder un module chacun de leur côté et que ces deux modules pouvaient être « assemblés » in fine pour donner naissance au programme de notre choix.

Même si ces deux modules renfermaient un objet ou une variable dont le nom était identique, il n’y avait pas de risques que le programme se comporte de manière erratique puisque chaque objet évoluait dans son propre espace de noms. C’est la raison pour laquelle  une commande telle que self.random_choice.random_choice ne levait pas d’exception. 

La notion d’espace de noms dans le langage Python

La notion d’espace de noms est très importante dans le langage Python. Lorsque nous définissons une fonction avec des variables à l’intérieur, ces dernières ne peuvent vivre que dans l’espace de noms de cette fonction. Dès que celle-ci a exécuté sa dernière ligne d’instruction, les variables qui y séjournent deviennent hors de portée. Voici un exemple :

#!/usr/bin/env python3
# -*- coding: utf8 -*-

def first_names():
    lady_1 = "Gabiche"
    print(lady_1)

first_names() #appel de fonction

Résultat : Gabiche.

La fonction affiche bien la valeur contenue dans la variable définie localement.

Maintenant, nous allons modifier la dernière ligne du programme. Au lieu d’appeler la fonction, nous allons tenter de faire un print() de la variable lady_1.

#!/usr/bin/env python3
# -*- coding: utf8 -*-

def first_names():
    lady_1 = "Gabiche"
    print(lady_1)

print(lady_1)

Traceback (most recent call last):
File « /home/ordinosor/1_exemple.py », line 8, in
print(lady_1)
NameError: name ‘lady_1’ is not defined

Godverdomme! Keskispasse? Eh bien, nous avons tout simplement essayé d’afficher la valeur d’une variable qui est hors de portée puisqu’elle est définie à l’intérieur de la fonction.

À présent, nous allons essayer de faire le contraire. Nous allons définir une variable lady_2  dite globale c’est-à-dire à l’extérieur de toute fonction, et dans la fonction first_names, nous allons tenter de faire un print(lady_2).

#!/usr/bin/env python3
# -*- coding: utf8 -*-

lady_2 = "Amandale"

def first_names():
    print(lady_2)

first_names() #Appel de fonction

Résultat : Amandale.

Là, dans cet exemple, lady_2 n’est pas définie à l’intérieur de la fonction, et pourtant le programme ne plante pas! Warum?

En fait, Python va d’abord chercher dans l’espace local une variable portant le nom de lady_2. S’il ne la trouve pas, alors il va poursuivre sa recherche à l’extérieur de la fonction, c’est-à-dire dans l’espace global où il existe bien une variable définie avec le nom lady_2. Par conséquent, la fonction print() peut récupérer la valeur de cette variable dite globale.

Maintenant, définissons deux variables portant le même nom mais ne stockant pas la même valeur… Une variable globale lady_2 et une variable locale lady_2 dans la fonction. Reprenons le code précédent:

#!/usr/bin/env python3
# -*- coding: utf8 -*-

lady_2 = "Amandale"

def first_names():
    lady_2 = "Mandy-Bulle"
    print(lady_2)

first_names() #Appel de fonction

Résultat : Mandy-Bulle.

Python a d’abord effectué une recherche en local, dans l’espace de noms de la fonction. Il a réussi à trouver une variable nommé lady_2. Donc, la recherche s’est immédiatement interrompue et la fonction print() a récupéré la valeur contenue dans cette variable locale, à savoir Mandy-Bulle.

Le mot-clé global

Il n’est pas possible de modifier la valeur de la variable globale lady_2 à l’intérieur de la fonction… A moins de lui affecter au préalable le mot-clé global mais cette technique est parfaitement déconseillée car dans un gros programme, si quelqu’un modifie la valeur stockée dans la variable, il y a un gros risque que ce changement impacte sans le vouloir d’autres parties du programme. Gros risque de bugs en perspective!

#!/usr/bin/env python3
# -*- coding: utf8 -*-

lady_2 = "Amandale"

def first_names():
    global lady_2
    lady_2 = "Mandy-Bulle"

print(lady_2) #Variable globale

Résultat : Mandy-Bulle.

Amandale a disparu du programme…

Et avec la méthode append() ?

Créons une liste ladies_list et essayons voir…

#!/usr/bin/env python3
# -*- coding: utf8 -*-

ladies_list = ["Amandale", "Gabiche"] 

def first_names():
    ladies_list.append("Mandy-Bulle")

first_names() #Appel de fonction
print(ladies_list) #Variable globale

Résultat : [« Amandale », « Gabiche », « Mandy-Bulle »].

Nous avons réussi à modifier la liste malgré le fait que la méthode append() soit présente à l’intérieur de la fonction. Pourquoi donc? Parce que nous avons fait appel à une méthode qui appartient à l’objet ladies_list. En agissant ainsi, aucune barrière d’espace de noms ne peut nous empêcher de mener à bien notre modification.

Mais il est impossible de modifier la variable globale ladies_list en lui affectant une nouvelle valeur à l’intérieur de la fonction.

#!/usr/bin/env python3
# -*- coding: utf8 -*-

ladies_list = ["Amandale", "Gabiche"] 

def first_names():
    ladies_list = ["Mandy-Bulle", "Dominille"]
    print(ladies_list)

first_names() #Appel de fonction
print(ladies_list) #Variable globale

[« Mandy-Bulle », « Dominille »] : Résutat qui correspond au print() de la variable locale ladies_list dans la fonction first_names (à la ligne n° 10).

[« Amandale », « Gabiche »] : Résultat qui correspond au print() de la variable globale ladies_list (à la ligne n° 11). Nous n’avons pas réussi à modifier cette dernière.

Avec le slicing

#!/usr/bin/env python3
# -*- coding: utf8 -*-

ladies_list = ["Amandale", "Gabiche"] 

def first_names():
    ladies_list[1:1] = ["Mandy-Bulle"]

first_names() #Appel de fonction
print(ladies_list) #Variable globale

Résultat : [« Amandale », « Mandy-Bulle », « Gabiche »]

Avec la technique du slicing, le résultat est positif car en fait, sans le savoir, nous utilisons la méthode constructeur de la classe slice. Elle agit directement sur l’objet et là encore, aucune barrière d’espace de noms ne peut nous empêcher de mener à bien notre modification. C’est comme si nous avions écrit ce code (ligne n° 7) :

#!/usr/bin/env python3
# -*- coding: utf8 -*-

ladies_list = ["Amandale", "Gabiche"] 

def first_names():
    ladies_list[slice(1,1)] = ["Mandy-Bulle"]

first_names() #Appel de fonction
print(ladies_list) #Variable globale

Attributs d’instance et attributs de classe

Il existe également une hiérarchie entre les espaces de noms des instances et des classes.

  • Les instances et les classes peuvent utiliser les variables définies au niveau global mais elles ne peuvent pas les modifier.

Dans le code ci-dessous, nous avons trois types de variables :

  • Une variable globale
  • Un attribut de classe
  • Un attribut d’instance
#!/usr/bin/env python3
# -*- coding: utf8 -*-

man_1 = "Dentifritz_1" #Variable globale

class FirstName :
    """Test avec des prénoms"""

    man_1 = "Dentifritz_2" #Attribut de classe

    def first_name(self):
        """test des variables"""

        man_1 = "Sébanislas"
        self.man_1 = "Barnabulle"

#===== MAIN ===================================================================

if __name__ == '__main__':

    test = FirstName()
    test.first_name()
    print(man_1) #Variable globale
    print(FirstName.man_1) #Attribut(ou variable) de classe
    print(test.man_1) #Attribut (ou variable) d'instance

Ligne n° 23 : Dentifritz_1

Ligne n° 24 : Dentifritz_2

Ligne n° 25 : Barnabulle

Je vous invite à modifier ce code et à faire autant de tests que nécessaire pour bien comprendre le concept d’espaces de noms et la hiérarchie qui les gouverne.

Auteur : Ordinosor

Bienvenue sur Miamondo, mon blog personnel. "Mia mondo", c'est de l'espéranto et ça signifie "Mon monde" en français. Je m'appelle Benoît alias Ordinosor, Français expatrié en Allemagne. Mes centres d'intérêt sont les distributions GNU/Linux, le langage de programmation Python, la science-fiction et l'espéranto.

2 réflexions sur « Python: Variables et espaces de noms »

  1. Une petite erreur à corriger : « Les instances peuvent utiliser les variables définies au niveau des classes et au niveau global mais elles ne peuvent pas les modifier. »

    Il est tout a fait possible de modifier une variable de classe depuis une instance, il n’y a aucune protection à ce niveau. Seules les globales sont protégées.
    Par exemple :
    class MaClasse():
    class_var = 42

    def change_class_var(self, val):
    MaClasse.class_var = val

    print(MaClasse.class_var)
    MaClasse().change_class_var(2017)
    print(MaClasse.class_var)

    Affichera :
    42
    2017

    C’est en fait comme pour le append sur une liste globale. Comme on accède à la variable de classe non pas directement par un nom, mais en tant que membre d’un objet connu (membre class_var de l’objet MaClasse), on a un accès en lecture et en écriture.

    En fait, pour bien expliquer ça, je pense qu’il aurait fallu introduire dans ce chapitre la notion de mutable/non mutable.

    Un nom est une étiquette sur un objet. Si cet objet est mutable, on peut le modifier quelque soit l’espace de nom dans lequel il se trouve (et uniquement le modifier, donc pas le remplacer par une autre instance d’objet mutable, d’où le fait que faire un append sur une liste marche, mais pas la remplacer par une nouvelle liste). S’il est non mutable, on ne peut le modifier que s’il est dans l’espace de nom local.

    Dans le cas de la variable de classe, l’objet auquel on accède est la classe, et comme une classe est mutable on peut modifier ses membres.

    J'aime

    1. Correction effectuée. Je prendrai le temps dans les jours qui viennent, d’introduire dans l’article, cette notion de mutable/non mutable. Ça rend en effet les choses plus claires.
      Merci pour cette précision.

      J'aime

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s