IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Portée des variables de classe


Sujet :

Python

  1. #1
    Membre régulier Avatar de Pierrot92320
    Homme Profil pro
    Ingénieur en retraite (électronique)
    Inscrit en
    Avril 2009
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Ingénieur en retraite (électronique)
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2009
    Messages : 159
    Points : 119
    Points
    119
    Par défaut Portée des variables de classe
    Bonjour

    Je suis actuellement plongé dans l'excellent livre "Apprendre à programmer avec Python 3" de G.S. A la page "espaces de noms des classes et instances" il est écrit que :
    --- Chaque classe possède son espace de noms (contenant les variables de classe), et chaque objet instance possède son espace de noms (contenant les variables d'instance).
    --- Les instances peuvent utiliser, mais pas modifier, les variables de classe.

    J'ai du mal à comprendre ce dernier point car en exécutant ce script :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    class Bidon(object):
        """essais de portée des variables"""
        var1 = 11       
     
    print(Bidon.var1)       # Variable de classe ?
     
    obj1 = Bidon()
    obj1.var1 = 0           # Variable d'instance ?
    print(obj1.var1)        # La variable d'instance est modifiée
     
    print(Bidon.var1)       # et la variable de classe aussi
     
    obj2 = Bidon()          # D'ailleurs les nouveaux objets ne sont plus
    print(obj1.var1)        # instanciés avec la même valeur de var1
    ... on constate que la variable Bidon.var1 a été modifiée par l'instance obj2.

  2. #2
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Je ne comprends pas; quand j'exécute ton code, à la ligne 12, la variable de classe vaut toujours 11, elle n'a pas été modifiée. Et la dernière ligne, je suppose que tu voulais écrire obj2.var1 (qui renvoie toujours 11 aussi) ?

    Par contre, je ne suis pas vraiment d'accord avec:
    --- Les instances peuvent utiliser, mais pas modifier, les variables de classe.
    Les instances peuvent modifier la variable de classe (Bidon.var1), mais il faut passer par la classe, pas par self. On ne peut pas la modifier en passant par self car cela crée une variable d'instance qui masque la variable de classe.

  3. #3
    Membre régulier Avatar de Pierrot92320
    Homme Profil pro
    Ingénieur en retraite (électronique)
    Inscrit en
    Avril 2009
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Ingénieur en retraite (électronique)
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2009
    Messages : 159
    Points : 119
    Points
    119
    Par défaut
    Citation Envoyé par dividee Voir le message
    On ne peut pas la modifier en passant par self car cela crée une variable d'instance qui masque la variable de classe.
    Excusez-moi. Vous avez raison, mon exemple est mauvais car je m'aperçois que j'ai mal interprété le problème.

    Le code suivant est plus fidèle au problème que je rencontre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Bidon(object):
        var1 = [11]
     
    obj1 = Bidon()
    obj2 = Bidon()
    print("obj1.var1 =", obj1.var1)     # Pour les deux instances 
    print("obj2.var1 =", obj2.var1)     # on a bien var1 = [11]
     
    obj1.var1[0] = 33                   # On modifie la variable
    print("obj1.var1 =", obj1.var1)     # d'instance de obj1 et celle de
    print("obj2.var1 =", obj2.var1)     # obj2 est également modifiée
     
    print("Bidon.var1 =", Bidon.var1)   # La variable de classe est 
    obj3 = Bidon()                      # également modifiée, ainsi
    print("obj3.var1 =", obj3.var1)     # que les nouvelles instances
    Il semble que ce comportement soit lié au type "liste" de var1. Car si je choisis le type entier cela fonctionne comme prévu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Bidon(object):
        var1 = 11
     
    obj1 = Bidon()
    obj2 = Bidon()
    print("obj1.var1 =", obj1.var1)     # Pour les deux instances 
    print("obj2.var1 =", obj2.var1)     # on a bien var1 = 11
     
    obj1.var1 = 33                      # On modifie la variable
    print("obj1.var1 =", obj1.var1)     # d'instance de obj1 et celle
    print("obj2.var1 =", obj2.var1)     # de obj2 n'est pas modifiée
     
    print("Bidon.var1 =", Bidon.var1)   # La variable de classe 
    obj3 = Bidon()                      # n'est pas modifiée ni
    print("obj3.var1 =", obj3.var1)     # les nouvelles instances

  4. #4
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 894
    Points : 7 250
    Points
    7 250
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Bidon(object):
        def __init__(self):
            self.var1 = [11]
     
    obj1 = Bidon()
    obj2 = Bidon()
    print("obj1.var1 =", obj1.var1)     # Pour les deux instances 
    print("obj2.var1 =", obj2.var1)     # on a bien var1 = [11]
     
    obj1.var1[0] = 33                   # On modifie la variable
    print("obj1.var1 =", obj1.var1)     # d'instance de obj1 et celle de
    print("obj2.var1 =", obj2.var1)     # obj2 est également modifiée
     
    print("Bidon.var1 =", Bidon.var1)   # La variable de classe est 
    obj3 = Bidon()                      # également modifiée, ainsi
    print("obj3.var1 =", obj3.var1)     # que les nouvelles instances
    Ça fera ce que vous voulez.

    En ne mettant pas self devant votre variable var, vous dites que var n'appartient pas à l'instance mais à la classe.

    Si vous modifiez une variable de classe, chaque instance créée sera effectuée en considérant la nouvelle valeur de var1.

    Maintenant si vous voulez modifier la variable d'une instance, vous devez exprimer l'appartenance de la variable var1 à l'objet à l'aide de self (convention comme this pour le C++ ou java)

  5. #5
    Membre averti
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2011
    Messages : 180
    Points : 321
    Points
    321
    Par défaut
    Bonjour

    Ceci est tout à fait normal, même si cela parait bizarre. En fait dans la classe
    ci-dessous :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class Maclasse(object):
        var1 = 2
        var2 = 'abc'
        var3 = (7,8)
        tableau = []
        dico = dict()
    var1, var2, var3 sont des variables de classes non mutables .
    C'est à dire que tout changement de valeur crée une nouvelle référence.
    A titre d'essais, utilise la fonction prédéfinie id(self.var1) par exemple. Tu constateras que, après modification, l'id a changé. C'est à dire que pour faire la modif demandée, python a effectué une copie de la variable avec une nouvelle valeur. L'ancienne valeur n'étant plus référencée, elle sera détruite par le garbage collector.

    En revanche, tableau et dico sont des variables mutables.
    Il n'y a pas une nouvelle instanciation à chaque modif.

    Quoi qu'il en soit, le bon "truc" pour modifier une variable de classe depuis une instance, c'est de faire quelque chose du style.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    a = Maclasse()
    a.__class__.var1 = 10
    a.__class__.tableau.append(18)
    Le code n'en sera que plus lisible.

  6. #6
    Membre averti
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2011
    Messages : 180
    Points : 321
    Points
    321
    Par défaut
    PS : Désolé, Fred, le temps que je réponde, tu avais apporté la tienne

  7. #7
    Membre régulier Avatar de Pierrot92320
    Homme Profil pro
    Ingénieur en retraite (électronique)
    Inscrit en
    Avril 2009
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Ingénieur en retraite (électronique)
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2009
    Messages : 159
    Points : 119
    Points
    119
    Par défaut
    Incroyable !

    Si, à la ligne 9 du script, je remplace
    par
    ou même par
    tout devient normal.

    Expliquez moi SVP

  8. #8
    Membre régulier Avatar de Pierrot92320
    Homme Profil pro
    Ingénieur en retraite (électronique)
    Inscrit en
    Avril 2009
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Ingénieur en retraite (électronique)
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2009
    Messages : 159
    Points : 119
    Points
    119
    Par défaut
    Merci Fred et chticricri.

    J'ai modifié mon texte alors que vous aviez commencé à préparer vos réponses excusez-moi je ne le ferai plus ! Je pense avoir compris vos explications, je vais méditer dessus.

  9. #9
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Quand tu écris obj1.var1[0] = 33, ça veut dire "remplacer le premier élément de la liste référencée dans l'attribut var1 de obj1 par 33". Tu ne modifies pas var1, mais l'objet (la liste, dans ce cas-ci) référencé par var1. var1 contient toujours la même liste, mais le contenu de cette liste a changé. En fait, si var1 était une variable de classe, cette ligne ne créerait pas de variable d'instance var1 dans obj1, mais irait chercher la variable de classe directement, car l'accès à var1 ne se fait qu'en lecture.

    obj1.var1 = [33], quant à lui, crée une nouvelle liste qui contient 33 et l'assigne à var1. Et comme c'est une écriture, il créerait dans ce cas une variable d'instance dans obj1 qui masquerait la variable de classe.

  10. #10
    Membre régulier Avatar de Pierrot92320
    Homme Profil pro
    Ingénieur en retraite (électronique)
    Inscrit en
    Avril 2009
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Ingénieur en retraite (électronique)
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2009
    Messages : 159
    Points : 119
    Points
    119
    Par défaut
    Citation Envoyé par dividee Voir le message
    Quand tu écris obj1.var1[0] = 33, ça veut dire "remplacer le premier élément de la liste référencée dans l'attribut var1 de obj1 par 33".
    Ok je comprends.
    var1 contient toujours la même liste, mais le contenu de cette liste a changé.
    Puis-je reformuler par "var1 pointe toujours sur la même liste". Le nom var1 est en effet la désignation d'un emplacement mémoire, et non l'emplacement mémoire lui-même. On pourrait en effet avoir un autre nom, comme var2, qui pointe vers le même emplacement mémoire.
    En fait, si var1 était une variable de classe, cette ligne ne créerait pas de variable d'instance var1 dans obj1, mais irait chercher la variable de classe directement, car l'accès à var1 ne se fait qu'en lecture.
    Là je ne comprends plus tellement.

    Dans le code ci-dessous var1 est une variable de classe et la ligne 8 modifie cette la variable, comme le montrent les lignes suivantes.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    >>> class Bidon(object):
        var1 = [11]
     
    >>> obj1 = Bidon()
    >>> print("obj1.var1 =", obj1.var1)
    obj1.var1 = [11]
    >>> 
    >>> obj1.var1[0] = 33
    >>> print("obj1.var1 =", obj1.var1)
    obj1.var1 = [33]
    >>> 
    >>> print("Bidon.var1 =", Bidon.var1)
    Bidon.var1 = [33]
    >>> 
    >>> obj2 = Bidon()
    >>> print("obj2.var1 =", obj2.var1)
    obj2.var1 = [33]
    A la lumière de ce que m'a expliqué chticricri plus haut, voici comment je vois les choses :

    L'objet obj1 se voit automatiquement attribuer une copie de la variable de classe var1 lors de son instanciation. Cette copie est une variable d'instance de obj1 et s'appelle aussi var1, mais elle n'est pas dans le même espace de noms. On peut donc théoriquement modifier la variable d'instance var1, sans que cela ne modifie la variable de classe var1.

    Je dis théoriquement, car dans le cas des variables de type mutable (ne dit-on pas modifiable en français ?) comme liste ou dictionnaire , ce n'est pas une vraie copie qui est effectuée. Dans ce cas en effet la variable d'instance obj1.var est un allias de la variable de classe Bidon.var, c'est à dire un nom différent qui pointe vers le même emplacement mémoire. L'instruction de la ligne 8 modifie donc la variable de classe.

    Par contre, si on écrit une instruction d'affectation, comme par exemple :
    Alors on crée une autre variable et celle-ci devient propre à l'instance obj1.

    Ai-je à peu près bien compris ?

  11. #11
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 894
    Points : 7 250
    Points
    7 250
    Par défaut
    Ce n'est qu'une question d'accès

    Liste ou pas ça change pas.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    >>> class A:
    ...     var = [5]
    ... 
    >>> a = A()
    >>> a.var = 2
    >>> a.var
    2
    >>> A.var
    [5]
    >>> A.var = [12]
    >>> A.var
    [12]
    >>> a.var
    2
    >>> A.var = 2
    >>> (A.var, a.var)
    (2, 2)
    Ce que je veux dire c'est que var et var ne sont pas les même variables

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    >>> A.var = [12]
    >>> a.var = [14]
    >>> (A.var, a.var)
    ([12], [14])

  12. #12
    Membre régulier Avatar de Pierrot92320
    Homme Profil pro
    Ingénieur en retraite (électronique)
    Inscrit en
    Avril 2009
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Ingénieur en retraite (électronique)
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2009
    Messages : 159
    Points : 119
    Points
    119
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    Ce n'est qu'une question d'accès. Liste ou pas ça change pas.
    J'ai compris ce mécanisme d'accès et de noms. Je suis également d'accord que le script que tu as écrit donnerait le même comportement si le type de la variable var affectée en ligne 2 n'était pas de type liste.

    Par contre on n'obtient plus le même comportement si on remplace dans ton script la ligne 2 par a.var[0] = 2 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> class A():
    	var = [5]
     
    >>> a = A()
    >>> a.var[0] = 2
    >>> a.var
    [2]
    >>> A.var
    [2]
    On voit en effet que dans ce cas, bien qu'on ai adressé la variable d'instance à la ligne 5, la variable de classe est également modifiée. Pour quelle raison ? D'après ce que j'ai compris, c'est parce que avec les types "Mutables" (terme qu'utilise chticricri un peu plus haut) comme liste ou dictionnaire, la variable a.var qui est créée au moment de l'instanciation de l'objet a n'est pas une vraie copie de A.var : dans ce cas a.var est seulement un allias de A.var et les deux noms pointent vers la variable de classe.

    C'est pourquoi l'instruction a.var[0] = 2 en ligne 6 a pour effet de modifier la variable de classe. Par contre une instruction d'affectation comme a.var = [2] ou comme a.var = 2 a pour effet de créer une véritable variable d'instance et ne modifie pas la variable de classe.

    Pourquoi, d'un point de vue sémantique, ces deux écritures ont-elles des effets différents ? Quels sont les motifs profonds de cette différence de comportement ? Pourrais-tu m'expliquer cela ? De plus pourquoi dit-on que les instances ne peuvent pas modifier les variables de classe alors que l'exemple ci-dessus semble prouver le contraire ?

  13. #13
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 326
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 326
    Points : 36 844
    Points
    36 844
    Par défaut
    Citation Envoyé par Pierrot92320 Voir le message
    Pourquoi, d'un point de vue sémantique, ces deux écritures ont-elles des effets différents ? Quels sont les motifs profonds de cette différence de comportement ? Pourrais-tu m'expliquer cela ? De plus pourquoi dit-on que les instances ne peuvent pas modifier les variables de classe alors que l'exemple ci-dessus semble prouver le contraire ?
    En Python, une variable est un label - une chaîne de caractère - associe a un objet. "associe" se traduit par un ensemble de cles/valeurs ou:
    • cle: label
    • valeur: objet

    appelé espace de noms.

    Lorsqu'on écrit var[0] = 1, Python fait deux opérations bien distinctes:
    • il récupère l'objet associe a "var",
    • *puis* applique la méthode __setitem__ a cet objet.

    Les espaces de noms sont organises de façon hiérarchique et ce qu'on appelle "portée des variables" devrait plutôt s'appeler mécanique de résolution de noms.

    Si on écrit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    var = [0]
    def func():
          var = 'a'
    l'appel a func() ne modifiera pas le "var" de l'espace de nom globals(). Il crée une variable locale clé="var" associée a l'objet="a" et tout sera détruit a la sortie de la fonction.
    Par contre, en écrivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    def func():
          var[0] = 'a'
    On essaie d'appliquer la méthode __setitem__ a l'objet associe a "var"... Rien dans locals(), on trouve une association dans globals(),
    la méthode est applique a l'objet associe... Et comme l'objet est mutable, l'objet est modifie.
    Assignez le tuple (0,) a var (dans globals()), on récupérera l'objet mais ça plantera: un tuple n'est pas mutable.

    La hiérarchie locals(), globals() s'applique aux variables "simples".

    Par défaut, dans une fonction, l'association d'un objet a une variable se fera dans l'espace de noms locals() de la fonction. Sinon, il faudra préciser nonlocal ou global.

    Classes et instances introduisent des règles semblables, sur d'autres espaces de noms et avec des règles de précédence ad hoc pour "résoudre", trouver ou créer l'objet associe.

    Mais gestion des espaces de noms et modification des objets associes sont des opérations bien distinctes. Penser "case mémoire", "par référence", "binding statique", comme pour les langages compiles ne fonctionne pas. Il faut penser: "espace de nommage" // "objets" // "binding dynamique"

    Relisez attentivement le tuto. Ca ne dit rien d'autre:
    ...
    Vous avez appris également que les instructions se trouvant à l'intérieur d'une fonction peuvent accéder aux variables définies au niveau principal, mais en consultation seulement : elles peuvent utiliser les valeurs de ces variables, mais pas les modifier (à moins de faire appel à l'instruction global).

    Il existe donc une sorte de hiérarchie entre les espaces de noms. Nous allons constater la même chose à propos des classes et des objets. En effet :

    Chaque classe possède son propre espace de noms. Les variables qui en font partie sont appelées variables de classe ou attributs de classe.
    Chaque objet instance (créé à partir d'une classe) obtient son propre espace de noms. Les variables qui en font partie sont appelées variables d'instance ou attributs d'instance.
    Les classes peuvent utiliser (mais pas modifier) les variables définies au niveau principal.
    Les instances peuvent utiliser (mais pas modifier) les variables définies au niveau de la classe et les variables définies au niveau principal.
    Il s'attache a comparer le comportement locals()/globals() avec celui des variables de classes/instances. Il a peut être juste oublie de préciser "par défaut" i.e. quand on écrit "self.var".

    - W

  14. #14
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 894
    Points : 7 250
    Points
    7 250
    Par défaut
    J'avoue que je n'utilise jamais cette syntaxe, mais c'est justement pour éviter ce cas de figure...

    C'est en effet une histoire de référence. Vous pouvez aller dans les méandres de la syntaxe python, mais l'explication est difficile.

    Pour être simple, python pour les types mutables, gère l'affectation par référence, plutôt que de faire une copie

  15. #15
    Membre régulier Avatar de Pierrot92320
    Homme Profil pro
    Ingénieur en retraite (électronique)
    Inscrit en
    Avril 2009
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Ingénieur en retraite (électronique)
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2009
    Messages : 159
    Points : 119
    Points
    119
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    On essaie d'appliquer la méthode __setitem__ a l'objet associe a "var"... Rien dans locals(), on trouve une association dans globals(), la méthode est applique a l'objet associe... Et comme l'objet est mutable, l'objet est modifie.
    Merci wiztricks pour ces explications qui cette fois m'éclairent. Ce que tu dis est effectivement expliqué dans le livre mais je n'avais pas vraiment saisi vraiment le sens.
    Il y reste un chose qui me turlupine. Dans le passage du livre que tu as cité, il est écrit :
    -- Les classes peuvent utiliser (mais pas modifier) les variables définies au niveau principal.
    -- Les instances peuvent utiliser (mais pas modifier) les variables définies au niveau de la classe et les variables définies au niveau principal.
    Or, comme tu le dis toi même, une fonction peut modifier une variable définie au niveau principal (si elle est mutable). De même, une classe peut modifier une variable définie au niveau principal (si elle est mutable) comme le montre cet exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    >>> a = [1]
    >>> class Bidon(object):
    	a[0] = 0
     
    >>> a
    [0]
    >>>
    J'admets parfaitement ce comportement. Tu l'as très bien expliqué et maintenant je le comprends. Ce que je ne comprends pas c'est pourquoi on dit qu'une fonction ou une classe ne peut pas modifier les variables définies au niveau principal, alors que c'est possible dans le cas des variables mutables.

  16. #16
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 326
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 326
    Points : 36 844
    Points
    36 844
    Par défaut
    Citation Envoyé par Pierrot92320 Voir le message
    Ce que je ne comprends pas c'est pourquoi on dit qu'une fonction ou une classe ne peut pas modifier les variables définies au niveau principal, alors que c'est possible dans le cas des variables mutables.
    Parce que vous n'avez pas encore compris!

    Effaçons tout et partons de plus bas.
    Un espace de nom est un mapping clés/valeurs.
    Ecrire:
    Equivaut a:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    >>> ns = dict()
    >>> ns['a'] = [1]
    Créons une entrée b a partir de "a":
    La valeur de ns['b'] est la même que celle de ns['a'].
    A cet endroit qu'on peut parler de référence: l'objet associe a l’entrée "a" de ns est aussi associe a l’entrée "b"
    Comme cet objet est "mutable", si on fait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    >>> ns['b'][0]='zz'
    >>> ns
    {'a': ['zz'], 'b': ['zz']}
    Qu'on passe par le chemin "a" ou "b", c'est toujours le même objet.

    Avec un objet non "mutable":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    >>> ns = dict()
    >>> ns['a'] = (0,)
    >>> ns['b'] = ns['a']
    >>> ns['b'][0] = 2
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment
    Revenez a votre exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    >>> a = [1]
    >>> class A:
    ...    a[0] = 'x'
    class A fait comme def: il crée un espace de noms locals().
    Et lorsque l’interpréteur rencontre a[0], il va chercher la définition de "a".
    Il la trouve dans le NS globals() et applique la méthode __setitem__ a l'objet retourné.
    Mais ça ne fait rien de plus que:
    c'est juste la fonction ns['b'] qui est un peu plus compliquée...
    Le tout cache dans une syntaxe n'obligeant pas a écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    >>> a = [1]
    >>> class A:
    ...    globals()['a'][0] = 'x'
    Le piège est que les objets ne sont que des choses auxquelles ont accède via les chemins définis via les espaces de nom.

    Le nombre d’entrées dans les espaces de nom qui "pointent" sur un même objet sont de vraies "références" au sens du code C avec lequel est construit l’interpréteur. Elles sont utilisées par le "garbage collector" pour détruire l'objet lorsque 0.

    global, local, variable de classe, d'instance sont des espaces de noms avec une portée, précédence et durée de vie définie.
    Ceci dit les objets sont toujours "globaux": la difficulté est de construire un chemin pour y accéder tant qu'ils sont "vivants".

    - W

  17. #17
    Membre régulier Avatar de Pierrot92320
    Homme Profil pro
    Ingénieur en retraite (électronique)
    Inscrit en
    Avril 2009
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Ingénieur en retraite (électronique)
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2009
    Messages : 159
    Points : 119
    Points
    119
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Parce que vous n'avez pas encore compris!
    Vraiment ? Cela m’inquiète que vous me disiez cela car j'ai pourtant l'impression d'avoir compris ... J'ai relu toutes vos explication et je n'y voit plus rien de mystérieux maintenant. Je pense que je n'ai pas bien posé ma question, il y a peut être un problème de mots quelque part, d'où l'importance de bien les choisir . Je vais essayer d'être plus précis.

    Dans ce bout de code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    a = [1]
    >>> class A():
    	globals()['a'][0] = 'x'
     
    >>> a
    ['x']
    1°) La variable a en ligne 1 est définie au niveau principal du script.
    2°) La même variable est modifiée à l'intérieur d'un bloc d'instructions faisant partie de la définition d'une classe.
    3°) On a donc modifiée une variable définie au niveau principal par une instruction située dans la définition d'une classe.
    4°) Ceci est en contradiction avec ce qu'on lit dans les manuels.

    Voilà. Je me doute qu'il y au moins une des 4 affirmations ci-dessus qui est fausse mais sincèrement je ne vois pas laquelle.

  18. #18
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 326
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 326
    Points : 36 844
    Points
    36 844
    Par défaut
    Citation Envoyé par Pierrot92320 Voir le message
    Cela m’inquiète que vous me disiez cela car j'ai pourtant l'impression d'avoir compris ...
    Si vos neurones ne sont pas pré-cables pour comprendre ce truc, c'est un chantier de plusieurs semaines...
    Et même lorsque vous aurez compris, vous vous ferez toujours piéger et vous devrez "redécouvrir"...

    1°) La variable a en ligne 1 est définie au niveau principal du script.
    2°) La même variable est modifiée à l'intérieur d'un bloc d'instructions faisant partie de la définition d'une classe.
    3°) On a donc modifiée une variable définie au niveau principal par une instruction située dans la définition d'une classe.
    4°) Ceci est en contradiction avec ce qu'on lit dans les manuels.

    Voilà. Je me doute qu'il y au moins une des 4 affirmations ci-dessus qui est fausse mais sincèrement je ne vois pas laquelle.
    Les 4 sont fausses
    Tant que vous ne séparez pas "variables" en "opérations" sur des espaces de noms et "méthodes" appliquées aux objets associes... vous gardez un mindset de programmeur C qu'il faut désapprendre (un peu) pour espérer comprendre ce qui se passe avec Python.

    Mais c'est pas grave... avancez dans le tuto, allez faire la fête...
    Les neurones se connectent pendant le sommeil: piétiner en se braquant dessus les fatiguent pour pas grand chose.

    - W

  19. #19
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 894
    Points : 7 250
    Points
    7 250
    Par défaut
    Bon je reviens avec la documentation, qui est claire

    Programmer’s note: Variables defined in the class definition are class variables; they are shared by all instances. To create instance variables, they can be set in a method with self.name = value. Both class and instance variables are accessible through the notation “self.name”, and an instance variable hides a class variable with the same name when accessed in this way. Class variables can be used as defaults for instance variables, but using mutable values there can lead to unexpected results. For new-style classes, descriptors can be used to create instance variables with different implementation details.
    Source

  20. #20
    Membre régulier Avatar de Pierrot92320
    Homme Profil pro
    Ingénieur en retraite (électronique)
    Inscrit en
    Avril 2009
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Ingénieur en retraite (électronique)
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2009
    Messages : 159
    Points : 119
    Points
    119
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Les 4 sont fausses
    Et non les 4 sont vraies. Le seul problème est que ce qui est écrit dans le manuel est incomplet.

    J'ai trouvé l'explication sur un site pourtant réputé pour être zéro. Je cite : une fonction ne peut modifier, par affectation, la valeur d'une variable extérieure à son espace local.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. [Débutant] Portée des variables dans une classe
    Par Jah73 dans le forum VB.NET
    Réponses: 24
    Dernier message: 10/10/2013, 14h55
  2. Portée des variables entre classes
    Par Gnifrus dans le forum Débuter avec Java
    Réponses: 2
    Dernier message: 03/03/2013, 14h37
  3. [POO] Portée des variables de classe
    Par guidav dans le forum Langage
    Réponses: 3
    Dernier message: 31/01/2007, 19h27
  4. [XSL]Problème de portée des variables
    Par djulesp dans le forum XSL/XSLT/XPATH
    Réponses: 6
    Dernier message: 17/09/2004, 10h34
  5. [Portée] portée des variables
    Par parksto dans le forum Langage
    Réponses: 7
    Dernier message: 09/05/2004, 21h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo