Ce que je veux, c'est ce comportement:
class a:
list = []
x = a()
y = a()
x.list.append(1)
y.list.append(2)
x.list.append(3)
y.list.append(4)
print(x.list) # prints [1, 3]
print(y.list) # prints [2, 4]
Bien sûr, ce qui se passe réellement lorsque j'imprime est:
print(x.list) # prints [1, 2, 3, 4]
print(y.list) # prints [1, 2, 3, 4]
De toute évidence, ils partagent les données en classe a
. Comment obtenir des instances distinctes pour obtenir le comportement que je souhaite?
list
comme nom d'attribut.list
est une fonction intégrée pour construire une nouvelle liste. Vous devez écrire les classes de noms avec une majuscule.Réponses:
Tu veux ça:
La déclaration des variables à l'intérieur de la déclaration de classe en fait des membres de "classe" et non des membres d'instance. Les déclarer dans la
__init__
méthode garantit qu'une nouvelle instance des membres est créée à côté de chaque nouvelle instance de l'objet, ce qui est le comportement que vous recherchez.la source
x.list = []
, vous pourriez alors le changer et n'affecter aucun autre. Le problème auquel vous êtes confronté est quex.list
et que vous avezy.list
la même liste, donc lorsque vous appelez append sur l'un, cela affecte l'autre.__init__
.La réponse acceptée fonctionne mais un peu plus d'explication ne fait pas de mal.
Les attributs de classe ne deviennent pas des attributs d'instance lorsqu'une instance est créée. Ils deviennent des attributs d'instance lorsqu'une valeur leur est attribuée.
Dans le code d'origine, aucune valeur n'est attribuée à l'
list
attribut après l'instanciation; il reste donc un attribut de classe. La définition de la liste à l'intérieur__init__
fonctionne car elle__init__
est appelée après l'instanciation. Alternativement, ce code produirait également la sortie souhaitée:Cependant, le scénario déroutant de la question n'arrivera jamais aux objets immuables tels que les nombres et les chaînes, car leur valeur ne peut pas être modifiée sans affectation. Par exemple, un code similaire à l'original avec le type d'attribut chaîne fonctionne sans aucun problème:
Donc, pour résumer: les attributs de classe deviennent des attributs d'instance si et seulement si une valeur leur est assignée après instanciation, qu'ils soient dans la
__init__
méthode ou non . C'est une bonne chose car de cette façon, vous pouvez avoir des attributs statiques si vous n'attribuez jamais de valeur à un attribut après l'instanciation.la source
Vous avez déclaré "liste" comme "propriété au niveau de la classe" et non comme "propriété au niveau de l'instance". Afin d'avoir des propriétés étendues au niveau de l'instance, vous devez les initialiser en les référençant avec le paramètre "self" dans la
__init__
méthode (ou ailleurs selon la situation).Il n'est pas strictement nécessaire d'initialiser les propriétés de l'instance dans la
__init__
méthode, mais cela facilite la compréhension.la source
Bien que la réponse acceptée soit parfaite, je voudrais ajouter une petite description.
Faisons un petit exercice
tout d'abord définir une classe comme suit:
Alors qu'avons-nous ici?
temp
qui est une chaîne__init__
méthode qui définitself.x
self.temp
Assez simple jusqu'à présent, oui? Maintenant, commençons à jouer avec cette classe. Initialisons d'abord cette classe:
Maintenant, procédez comme suit:
Eh bien, a
a.temp
fonctionné comme prévu, mais comment diable a-t-ilA.temp
fonctionné? Eh bien, cela a fonctionné parce que temp est un attribut de classe. Tout en python est un objet. Ici A est aussi un objet de classetype
. Ainsi, l'attribut temp est un attribut détenu par laA
classe et si vous modifiez la valeur de temp viaA
(et non via une instance dea
), la valeur modifiée sera reflétée dans toute l'instance deA
classe. Allons-y et faisons cela:Intéressant n'est-ce pas? Et notez que
id(a.temp)
etid(A.temp)
sont toujours les mêmes .Tout objet Python reçoit automatiquement un
__dict__
attribut, qui contient sa liste d'attributs. Examinons ce que contient ce dictionnaire pour nos exemples d'objets:Notez que l'
temp
attribut est répertorié parmiA
les attributs de la classe tandis qu'ilx
est répertorié pour l'instance.Alors, comment se fait-il que nous obtenions une valeur définie
a.temp
si elle n'est même pas répertoriée pour l'instancea
. Eh bien, c'est la magie de la__getattribute__()
méthode. En Python, la syntaxe pointillée appelle automatiquement cette méthode, donc lorsque nous écrivonsa.temp
, Python s'exécutea.__getattribute__('temp')
. Cette méthode exécute l'action de recherche d'attribut, c'est-à-dire trouve la valeur de l'attribut en regardant à différents endroits.L'implémentation standard de
__getattribute__()
recherche d'abord le dictionnaire interne ( dict ) d'un objet, puis le type de l'objet lui-même. Dans ce casa.__getattribute__('temp')
s'exécute d'aborda.__dict__['temp']
, puisa.__class__.__dict__['temp']
Bon maintenant, utilisons notre
change
méthode:Eh bien maintenant que nous l'avons utilisé
self
,print(a.temp)
nous donne une valeur différente deprint(A.temp)
.Maintenant, si nous comparons
id(a.temp)
etid(A.temp)
, ils seront différents.la source
Oui, vous devez déclarer dans le "constructeur" si vous voulez que la liste devienne une propriété d'objet et non une propriété de classe.
la source
Donc presque toutes les réponses ici semblent manquer un point particulier. Les variables de classe ne deviennent jamais des variables d' instance comme le montre le code ci-dessous. En utilisant une métaclasse pour intercepter l'affectation de variable au niveau de la classe, nous pouvons voir que lorsqu'un.myattr est réaffecté, la méthode magique d'affectation de champ sur la classe n'est pas appelée. En effet, l'affectation crée une nouvelle variable d'instance . Ce comportement n'a absolument rien à voir avec la variable de classe comme démontré par la deuxième classe qui n'a pas de variables de classe et qui permet encore l'affectation de champ.
EN BREF Les variables de classe n'ont RIEN à voir avec les variables d'instance.
Plus clairement, ils se trouvent juste dans la portée des recherches sur les instances. Les variables de classe sont en fait des variables d'instance sur l'objet de classe lui-même. Vous pouvez également avoir des variables de métaclasse si vous le souhaitez, car les métaclasses elles-mêmes sont également des objets. Tout est un objet, qu'il soit utilisé pour créer d'autres objets ou non, donc ne soyez pas lié à la sémantique des autres langages d'utilisation de la classe de mots. En python, une classe n'est en réalité qu'un objet utilisé pour déterminer comment créer d'autres objets et quels seront leurs comportements. Les métaclasses sont des classes qui créent des classes, juste pour illustrer davantage ce point.
la source
Pour protéger votre variable partagée par une autre instance, vous devez créer une nouvelle variable d'instance à chaque fois que vous créez une instance. Lorsque vous déclarez une variable à l'intérieur d'une classe, c'est une variable de classe et partagée par toutes les instances. Si vous voulez le faire par exemple, vous devez utiliser la méthode init pour réinitialiser la variable en vous référant à l'instance
À partir des objets et de la classe Python de Programiz.com :
Par exemple:
la source