J'ai un problème pour comprendre comment les variables de classe/instance fonctionnent en Python. Je ne comprends pas pourquoi lorsque j'essaie ce code, la variable de liste semble être une variable de classe
class testClass():
list = []
def __init__(self):
self.list.append('thing')
p = testClass()
print p.list
f = testClass()
print f.list
Production:
['thing']
['thing', 'thing']
et quand je fais cela, il semble être une variable d'instance
class testClass():
def __init__(self):
self.list = []
self.list.append('thing')
p = testClass()
print p.list
f = testClass()
print f.list
Production:
['thing']
['thing']
Cela est dû à la façon dont Python résout les noms avec le .
. Lorsque vous écrivez self.list
le Python essaie de résoudre le nom list
d'abord en le recherchant dans l'objet instance, et s'il n'y est pas trouvé, puis dans l'instance de classe.
Examinons-le pas à pas
self.list.append(1)
list
dans l'objet self
? list
dans l'instance de classe de l'objet self
? Mais lorsque vous liez un nom, les choses sont différentes:
self.list = []
list
dans l'objet self
? Donc, c'est toujours une variable d'instance.
Votre premier exemple crée un list
dans l'instance de classe, car il s'agit de la portée active à l'époque (pas de self
n'importe où). Mais votre deuxième exemple crée un list
explicitement dans la portée de self
.
Plus intéressant serait l'exemple:
class testClass():
list = ['foo']
def __init__(self):
self.list = []
self.list.append('thing')
x = testClass()
print x.list
print testClass.list
del x.list
print x.list
Cela imprimera:
['thing']
['foo']
['foo']
Au moment où vous supprimez le nom de l'instance, le nom de la classe est visible via la référence self
.
Python a des règles intéressantes sur la recherche de noms. Si vous voulez vraiment vous décourager, essayez ce code:
class testClass():
l = []
def __init__(self):
self.l = ['fred']
Cela donnera à chaque instance une variable appelée l
qui masque la variable de classe l
. Vous pourrez toujours accéder à la variable de classe si vous le faites self.__class__.l
.
La façon dont j'y pense est la suivante ... Chaque fois que vous le faites instance.variable
(même pour les noms de méthode, ce ne sont que des variables dont les valeurs se trouvent être des fonctions) il le recherche dans le dictionnaire de l'instance. Et s'il ne le trouve pas, il essaie de le rechercher dans le dictionnaire de classe de l'instance. C'est seulement si la variable est en cours de "lecture". S'il est affecté à, il crée toujours une nouvelle entrée dans le dictionnaire d'instances.
Dans votre premier exemple, list
est un attribut de la classe, partagé par toutes ses instances. Cela signifie que vous pouvez même y accéder sans avoir un objet de type testClass
:
>>> class testClass():
... list = []
... def __init__(self):
... self.list.append("thing")
...
>>> testClass.list
[]
>>> testClass.list.append(1)
>>> testClass.list
[1]
Mais tous les objets partagent l'attribut list
avec la classe et entre eux:
>>> testObject = testClass()
>>> testObject.list
[1, 'thing']
>>> testClass.list
[1, 'thing']
>>>
>>> testObject2 = testClass()
>>> testClass.list
[1, 'thing', 'thing']
>>> testObject2.list
[1, 'thing', 'thing']
Lorsque vous instanciez une classe, __init__
la méthode est exécutée automatiquement.
Dans le premier cas, votre liste est un attribut de classe et est partagée par toutes ses instances. Vous avez deux choses parce que vous en avez ajouté une lors de l'instanciation de p
et une autre lors de l'instanciation de f
(la première a déjà été ajoutée lors du premier appel).