2010-06-30 5 views
10

Je suis encore relativement nouveau pour Python, et mon expérience OO vient de Java. J'ai donc un code que j'ai écrit en Python qui est agissant très inhabituel pour moi, étant donné le code suivant:Étendue et listes de classe Python

class MyClass(): 
     mylist = [] 
     mynum = 0 

     def __init__(self): 
       # populate list with some value. 
       self.mylist.append("Hey!") 
       # increment mynum. 

       self.mynum += 1 

a = MyClass() 

print a.mylist 
print a.mynum 

b = MyClass() 

print b.mylist 
print b.mynum 

L'exécution de ce qui donne le résultat suivant:

['Hey!'] 
1 
['Hey!', 'Hey!'] 
1 

De toute évidence, je me attends les variables de classe pour aboutir aux mêmes données exactes, et la même sortie exacte ... Ce que je ne trouve nulle part est ce qui rend une liste différente de dire une chaîne ou un nombre, pourquoi la liste se référant à la même liste de la première instanciation dans les suivantes? Il est clair que je suis probablement en train de me méprendre sur une sorte de mécanique de portée ou de mécanique de création de liste.

Répondre

8

La réponse de tlayton fait partie de l'histoire, mais elle n'explique pas tout.

Ajouter un

print MyClass.mynum 

pour devenir encore plus confus :). Il va imprimer '0'. Pourquoi?Parce que la ligne

self.mynum += 1 

crée une variable d'instance et augmente ultérieurement. Il n'augmente pas la variable classe.

L'histoire de la mylist est différente.

self.mylist.append("Hey!") 

ne sera pas créer une liste. Il s'attend à ce qu'une variable avec une fonction 'append' existe. Puisque l'instance ne possède pas une telle variable, elle finit par se référer à celle de la classe, laquelle existe, puisque vous l'avez initialisée. Tout comme dans Java, une instance peut implicitement référencer une variable de classe. Un avertissement comme 'Les champs de classe devraient être référencés par la classe, pas par une instance' (ou quelque chose comme ça, ça fait un moment que je l'ai vu en Java) serait en ordre. Ajouter une ligne

print MyClass.mylist 

pour vérifier cette réponse :). En bref: vous initialisez des variables de classe et mettez à jour des variables d'instance. Les instances peuvent référencer des variables de classe, mais certaines instructions 'update' créeront automagiquement les variables d'instance pour vous.

+0

Cela manque toujours le point clé; Voir la réponse de Ken. 'self.mynum + = 1' se développe en' self.mynum = self.mynum + 1'. Lorsque cela est exécuté, le côté droit est d'abord évalué. 'self.mynum' recherche' mynum' sur les instances, et ne parvient pas à le trouver (au moins la première fois que '__init__' est appelé), alors il recherche (et trouve)' mynum' sur la classe. Ainsi, 'self.mynum' sur le côté droit évalue à la valeur de' MyClass.mynum'. Cette valeur est maintenant affectée à 'self.mynum', qui se réfère ici à la variable d'instance. –

5

Ce que vous faites ici ne consiste pas simplement à créer une variable de classe. En Python, les variables définies dans le corps de la classe résultent à la fois d'une variable de classe ("MyClass.mylist") et d'une variable d'instance ("a.mylist"). Ce sont des variables séparées, pas seulement des noms différents pour une seule variable. Toutefois, lorsqu'une variable est initialisée de cette manière, la valeur initiale n'est évaluée qu'une seule fois et transmise aux variables de chaque instance. Cela signifie que, dans votre code, la variable mylist de chaque instance de MyClass fait référence à un seul objet liste. La différence entre une liste et un nombre dans ce cas est que, comme dans Java, les valeurs primitives telles que les nombres sont copiées lorsqu'elles sont transmises d'une variable à une autre. Cela entraîne le comportement que vous voyez; Même si l'initialisation de la variable n'est évaluée qu'une seule fois, le 0 est copié lorsqu'il est transmis à la variable de chaque instance. En tant qu'objet, cependant, la liste ne fait rien de tel, donc vos appels append() proviennent tous de la même liste. Essayez ceci à la place:

class MyClass(): 
    def __init__(self): 
     self.mylist = ["Hey"] 
     self.mynum = 1 

Cela entraînera l'évaluation de la valeur séparément chaque fois qu'une instance est créée. Contrairement à Java, vous n'avez pas besoin des déclarations de corps de classe pour accompagner cet extrait; les affectations dans le __init __() servent comme toute la déclaration qui est nécessaire.

+1

"Ce sont des variables séparées, pas seulement des noms différents pour une seule variable." <- Je ne pense pas que ce soit vrai. Essayez de faire 'id (a.mylist)' et 'id (MyClass.mylist)' et voyez ce que vous obtenez. –

+0

Ce sont des variables séparées, faisant la distinction importante entre les variables et les valeurs. ces deux appels à id() retourneront égal, parce que les deux a.mylist et MyClass.mylist se réfèrent au même objet (créé lors de l'évaluation de la ligne "mylist = []", mais ils sont toujours séparés, assignant une liste complètement différente – tlayton

+0

@Mark: Vous avez raison de dire que ce n'est pas vrai: les deux premières lignes de la classe créent des variables de classe et seulement des variables de classe.Voir ma réponse pour l'histoire correcte Cependant, votre méthode pour montrer que ce n'est pas vrai ne fonctionne pas: l'instance peut référencer la variable de classe et ces deux commandes retourneront en fait le même identifiant, mais quand elles seront répétées pour la variable 'mynum', elles – Confusion

5

Je crois que la différence est que += est une mission (la même chose que = et +), alors que append change un objet en place.

mylist = [] 
    mynum = 0 

Ceci affecte certaines variables de classe, une fois, à l'heure de définition de la classe.

self.mylist.append("Hey!") 

Cela change la valeur MyClass.mylist par une chaîne annexant.

self.mynum += 1 

Ceci est le même que self.mynum = self.mynum + 1, à savoir, il assigne (membre d'instance) self.mynum. La lecture de self.mynum passe au membre de la classe, car à ce moment-là il n'y a aucun membre d'instance de ce nom.

+0

Techniquement, MyClass.mylist n'est pas une valeur. Comme self.mylist, c'est une variable. L'appel de self.mylist.append() ne modifie pas la valeur de MyClass.mylist; il modifie la liste à laquelle les deux variables se réfèrent. – tlayton

+0

+1. Bonne réponse. @tlayton: Que voulez-vous dire quand vous dites «valeur»? Je dirais certainement que l'appel 'self.mylist.append (" Hey! ")' Modifie la valeur de 'MyClass.mylist'. –

+0

Pas selon ma compréhension de la signification de «valeur» dans ce contexte. La valeur de MyClass.mylist est la liste à laquelle il se réfère. Appeler append() ne fait pas mylist se référer à une liste différente, mais modifie la liste elle-même. J'interprète probablement un peu trop "valeur" techniquement, cependant. – tlayton

Questions connexes