Ich habe eine Frage zu __class__
in Python.
Die Dokumentation besagt, dass __class__
die Klasse ist, zu der eine Klasseninstanz gehört. Also habe ich eine Reihe von Experimenten durchgeführt:
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
Alles läuft gut.
Dann gebe ich den Code wie folgt ein:
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
Aus dem obigen Code dachte ich, dass NewCounter1.count
vielleicht NewCounter1
oder __class__.count
ist, aber der folgende Code hat mich überrascht:
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
Warum hat sich NewCounter2.count
geändert, aber NewCounter2.__class__.count
bleibt bei 3? Wenn ich NewCounter2.count
änderte, wurde NewCounter2.__class__.count is NewCounter2.count
False
. Was in der Welt ist das Attribut __class__
"Nach den obigen Codes dachte ich, dass NewCounter1.count vielleicht NewCounter1 ist. _Class_.count"
Das Problem ist, dass zum Zeitpunkt dieses Satzes in Ihrer Frage nach den einzigen Anweisungen:
NewCounter1 = counter()
NewCounter2 = counter()
NewCounter2.__class__.count = 3
NewCounter1 und NewCounter2 erstellt haben
und das Klassenattribut counter.count geändert hat,
Es gibt keine Objekte NewCounter1.count und NewCounter2.count, und dann hat "equals" keine wirkliche Bedeutung.
.
Siehe die Erstellung von NewCounter1 und gleich danach:
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_ ist der Namespace der Instanz NewCounter1print NewCounter1.count
druckt dasselbe wie print counter.count
'Count' (die Zeichenfolge 'count') befindet sich jedoch nicht im Namespace von NewCounter1, dh es befindet sich kein Attribut count im Namespace der erstellten Instanz!
Wie ist es möglich ?
Das liegt daran, dass die Instanz ohne Zuordnung zu einer 'count'-Kennung innerhalb von INIT erstellt wird.
-> In NewCounter1 gibt es keine reale Erstellung eines Attributs als Feld, d. h. keine Erstellung des INSTANCE-Attributs.
Die Folge ist das bei der Anweisungprint 'NewCounter1.count ==',NewCounter1.count
ausgewertet wird, findet der Interpreter kein Instanzattribut im Namespace von NewCounter1 und geht dann zur Klasse der Instanz, um nach dem Schlüssel 'count' im Namespace dieser Klasse zu suchen. Dort findet es "count" als Schlüssel eines CLASS-Attributs und kann den VALUE des Objekts counter.count als VALUE nehmen, um als Antwort auf die Anweisung anzuzeigen.
Eine Klasseninstanz verfügt über einen Namespace, der als Wörterbuch implementiert ist, nämlich der erste Ort, an dem Attributreferenzen gesucht werden. Wenn ein Das Attribut wird dort nicht gefunden, und die Klasse der Instanz hat ein Attribut mit diesem Namen, wird die Suche mit der Klasse .__ fortgesetzt. Attribute . http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy
NewCounter1.count equals NewCounter1.__class__.count
bedeutet hier also, dass der VALUE für NewCounter1.count, auch wenn dieser nicht wirklich existiert, der VALUE des Klassenattributs NewCounter1 ist. Klasse. Anzahl. Hier ist "ist" das englische Verb, nicht das Merkmal ist der Sprache, die die Identitäten von zwei Objekten testet, und es bedeutet "wird als" betrachtet.
Wenn NewCounter2.__class__.count = 3
ausgeführt wird, ist nur das Klassenattribut counter.count betroffen. Die Namespaces von NewCounter1 und NewCounter2 bleiben leer, und es wird derselbe Mechanismus verwendet, mit dem der Wert von counter.count in der Klasse gesucht wird.
.
Am Ende, wenn NewCounter2.count = 5
ausgeführt wird, wird diesmal ein INSTANCE-Attribut count als Feld im NewCounter2 - Objekt erstellt und 'count' erscheint im Namensraum von NewCounter2.
Es wird nichts überschrieben, da im __dict__
der Instanz nichts vorangestellt war.
Keine andere Änderung wirkt sich auf NewCounter1 und counter.count aus.
Der folgende Code zeigt explizit die zugrunde liegenden Ereignisse während der Ausführung:
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)
ergebnis
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
.
Eine interessante Sache ist das Hinzufügen einer Anweisungself.count = counter.count
VOR der Zeileself.__class__.count += 1 # <<=====
um die Veränderung der Ergebnisse zu beobachten
.
Zusammenfassend war der Punkt nicht __class__
, sondern der Mechanismus der Suche nach einem Attribut, und dieser Mechanismus ist irreführend, wenn er ignoriert wird.
Diese Linie:
NewCounter2.__class__.count = 3
ändert die statische count
von counter
, aber hier:
NewCounter2.count = 5
NewCounter2
hat jetzt ein eigenes count
-Attribut, das die statische count
verbirgt;
Diese Zeile hat keine Auswirkung auf NewCounter1
.
Deshalb auch NewCounter2.__class__.count != NewCounter2.count
.
Das erneute Binden (d. H. Zuweisen) eines Attributs in einem Objekt mit demselben Namen wie ein Attribut in der Klasse spiegelt das Attribut in der Klasse wider Das Objekt wird immer zuerst auf Attribute geprüft, gefolgt von den Klassen in MRO-Reihenfolge.
Linie
NewCounter2.count = 5
Erstellt ein neues Instanzebene Attribut von NewCounter2
. Danach greifen Sie auf zwei verschiedene Attribute zu (NewCounter2.count
- Instanzebene attr und NewCounter2.__class__.count
- Klassenebene attr), was zu einem 'seltsamen' Verhalten 'führt.