web-dev-qa-db-de.com

Was ist das Attribut "__class__" in Python?

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.countFalse. Was in der Welt ist das Attribut __class__

30
Searene

"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 NewCounter1
print 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 Anweisung
print '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 Anweisung
self.count = counter.count
VOR der Zeile
self.__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.

21
eyquem

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.

10
chown

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.

0
Andriy Ivaneyko