J'ai une question à propos de __class__
en python.
La documentation indique que __class__
est la classe à laquelle appartient une instance de classe. J'ai donc mené une série d'expériences:
class counter:
count = 0
def __init__(self):
self.__class__.count += 1
NewCounter1 = counter()
print NewCounter1.count #The result is 1
NewCounter2 = counter()
print NewCounter2.count #The result is 2
print NewCounter2.__class__.count is NewCounter2.count #result: True
Tout va bien.
Puis j'entre le code comme suit:
NewCounter2.__class__.count = 3
print NewCounter1.count #result:3
print NewCounter1.__class__.count #result:3
print NewCounter2.count #result:3
print NewCounter2.__class__.count #result:3
print NewCounter2.__class__.count is NewCounter2.count #result: True
D'après le code ci-dessus, j'ai pensé que peut-être NewCounter1.count
est égal à NewCounter1
ou __class__.count
, mais le code suivant m'a surpris
NewCounter2.count = 5
print NewCounter1.count #result:3
print NewCounter1.__class__.count #result:3
print NewCounter2.count #result:5
print NewCounter2.__class__.count #result:3
print NewCounter2.__class__.count is NewCounter2.count #result: False
Pourquoi NewCounter2.count
a-t-il changé mais NewCounter2.__class__.count
est resté à 3? De plus, lorsque j'ai changé NewCounter2.count
, NewCounter2.__class__.count is NewCounter2.count
est devenu False
. Qu'est-ce qui est dans le monde l'attribut __class__
"D'après les codes ci-dessus, j'ai pensé que peut-être NewCounter1.count est égal à NewCounter1. _Class_.count"
Le problème est qu’au moment de cette phrase de votre question, après les seules instructions:
NewCounter1 = counter()
NewCounter2 = counter()
NewCounter2.__class__.count = 3
ayant créé NewCounter1 et NewCounter2
et ayant modifié l'attribut de classe counter.count,
il n'y a pas d'objets NewCounter1.count ni NewCounter2.count, puis "égal" n'a pas de véritable sens.
.
Voir la création de NewCounter1 et juste après:
class counter:
count = 0
def __init__(self):
self.__class__.count += 1
print 'counter.count BEFORE ==',counter.count # The result is 0
NewCounter1 = counter()
print '\nNewCounter1.__dict__ ==',NewCounter1.__dict__ # The result is {}
print 'NewCounter1.count ==',NewCounter1.count # The result is 1
print 'counter.count AFTER ==',counter.count # The result is 1
NewCounter ._dict_ est l'espace de noms de l'instance NewCounter1print NewCounter1.count
imprime le même que print counter.count
Cependant, 'count' (la chaîne 'count') n'est pas dans l'espace de nommage de NewCounter1, c'est-à-dire qu'il n'y a pas d'attribut count dans l'espace de nommage de l'instance créée!
Comment est-ce possible ?
En effet, l'instance est créée sans assignation à un identifiant 'count' à l'intérieur du INIT
-> il n'y a pas de création réelle d'attribut en tant que champ dans NewCounter1, c'est-à-dire pas de création d'attribut INSTANCE.
La conséquence est que lorsque l'instructionprint 'NewCounter1.count ==',NewCounter1.count
est évalué, l'interprète ne trouve pas d'attribut d'instance dans l'espace de nommage de NewCounter1, puis se rend dans la classe de l'instance pour rechercher la clé 'count' dans l'espace de nommage de cette classe; là, il trouve 'count' en tant que clé d'un attribut CLASS et peut prendre la valeur de l'objet counter.count en tant que valeur à afficher en réponse à l'instruction.
Une instance de classe a un espace de noms implémenté sous la forme d'un dictionnaire qui est le premier endroit où les références d'attribut sont recherchées. Quand un attribut n’y est pas trouvé et la classe de l’instance a un attribut par ce nom, la recherche continue avec la classe les attributs. http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy
Donc, NewCounter1.count equals NewCounter1.__class__.count
signifie ici que la valeur de NewCounter1.count, même si celle-ci n'existe pas réellement, est la valeur de l'attribut de classe NewCounter1. classe. compte. Ici "is" est le verbe anglais, pas la caractéristique est du langage qui teste l’identité de deux objets, et cela signifie "est considéré comme ayant"
Lorsque NewCounter2.__class__.count = 3
est exécuté, seul l'attribut de classe counter.count est affecté. Les espaces de noms NewCounter1 et NewCounter2 restent vides et le même mécanisme consistant à se rendre dans la classe pour trouver la valeur de counter.count est suivi.
.
À la fin, lorsque NewCounter2.count = 5
est exécuté, un attribut INSTANCE count est créé en tant que champ dans l'objet NewCounter2 et «count» apparaît dans l'espace de nommage de NewCounter2.
Il n’écrase rien car il n'y avait rien de précédent dans le __dict__
de l’instance
Aucun autre changement n'affecte NewCounter1 et counter.count
Le code suivant montre plus explicitement les événements sous-jacents lors de l'exécution:
from itertools import islice
class counter:
count = 0
def __init__(self):
print (' | counter.count first == %d at %d\n'
' | self.count first == %d at %d')\
% (counter.count,id(counter.count),
self.count,id(self.count))
self.__class__.count += 1 # <<=====
print (' | counter.count second == %d at %d\n'
' | self.count second == %d at %d\n'
' | id(counter) == %d id(self) == %d')\
% (counter.count,id(counter.count),
self.count,id(self.count),
id(counter),id(self))
def display(*li):
it = iter(li)
for ch in it:
nn = (len(ch)-len(ch.lstrip('\n')))*'\n'
x = it.next()
print '%s == %s %s' % (ch,x,'' if '__dict__' in ch else 'at '+str(id(x)))
display('counter.count AT START',counter.count)
print ('\n\n----- C1 = counter() ------------------------')
C1 = counter()
display('C1.__dict__',C1.__dict__,
'C1.count ',C1.count,
'\ncounter.count ',counter.count)
print ('\n\n----- C2 = counter() ------------------------')
C2 = counter()
print (' -------------------------------------------')
display('C1.__dict__',C1.__dict__,
'C2.__dict__',C2.__dict__,
'C1.count ',C1.count,
'C2.count ',C2.count,
'C1.__class__.count',C1.__class__.count,
'C2.__class__.count',C2.__class__.count,
'\ncounter.count ',counter.count)
print '\n\n------- C2.__class__.count = 3 ------------------------\n'
C2.__class__.count = 3
display('C1.__dict__',C1.__dict__,
'C2.__dict__',C2.__dict__,
'C1.count ',C1.count,
'C2.count ',C2.count,
'C1.__class__.count',C1.__class__.count,
'C2.__class__.count',C2.__class__.count,
'\ncounter.count ',counter.count)
print '\n\n------- C2.count = 5 ------------------------\n'
C2.count = 5
display('C1.__dict__',C1.__dict__,
'C2.__dict__',C2.__dict__,
'C1.count ',C1.count,
'C2.count ',C2.count,
'C1.__class__.count',C1.__class__.count,
'C2.__class__.count',C2.__class__.count,
'\ncounter.count ',counter.count)
résultat
counter.count AT START == 0 at 10021628
----- C1 = counter() ------------------------
| counter.count first == 0 at 10021628
| self.count first == 0 at 10021628
| counter.count second == 1 at 10021616
| self.count second == 1 at 10021616
| id(counter) == 11211248 id(self) == 18735712
C1.__dict__ == {}
C1.count == 1 at 10021616
counter.count == 1 at 10021616
----- C2 = counter() ------------------------
| counter.count first == 1 at 10021616
| self.count first == 1 at 10021616
| counter.count second == 2 at 10021604
| self.count second == 2 at 10021604
| id(counter) == 11211248 id(self) == 18736032
-------------------------------------------
C1.__dict__ == {}
C2.__dict__ == {}
C1.count == 2 at 10021604
C2.count == 2 at 10021604
C1.__class__.count == 2 at 10021604
C2.__class__.count == 2 at 10021604
counter.count == 2 at 10021604
------- C2.__class__.count = 3 ------------------------
C1.__dict__ == {}
C2.__dict__ == {}
C1.count == 3 at 10021592
C2.count == 3 at 10021592
C1.__class__.count == 3 at 10021592
C2.__class__.count == 3 at 10021592
counter.count == 3 at 10021592
------- C2.count = 5 ------------------------
C1.__dict__ == {}
C2.__dict__ == {'count': 5}
C1.count == 3 at 10021592
C2.count == 5 at 10021568
C1.__class__.count == 3 at 10021592
C2.__class__.count == 3 at 10021592
counter.count == 3 at 10021592
.
Une chose intéressante à faire est d'ajouter une instructionself.count = counter.count
AVANT la ligneself.__class__.count += 1 # <<=====
.__ pour observer l'évolution des résultats
.
En conclusion, le point ne concernait pas __class__
mais le mécanisme de recherche d'un attribut, et ce mécanisme est trompeur lorsqu'il est ignoré.
Cette ligne:
NewCounter2.__class__.count = 3
change la valeur statique count
de counter
, mais ici:
NewCounter2.count = 5
NewCounter2
a maintenant son propre attribut count
qui cache la valeur statique count
;
Cette ligne n'a donc aucun effet sur NewCounter1
.
C'est aussi pourquoi NewCounter2.__class__.count != NewCounter2.count
.
La nouvelle liaison (c'est-à-dire l'affectation) d'un attribut sur un objet portant le même nom qu'un attribut sur la classe masque l'attribut sur la classe. L'objet est toujours vérifié en premier pour les attributs, suivi des classes dans l'ordre MRO.
Ligne
NewCounter2.count = 5
Crée un nouveau niveau d'instance attribut de NewCounter2
. Après cela, vous accédez à deux attributs différents (NewCounter2.count
- attr au niveau instance et NewCounter2.__class__.count
- attr au niveau classe) qui provoquent un comportement "étrange".