2009-11-08 7 views
10

Eine zufällige Klassendefinition:Unterschiede zwischen statischen und Instanzvariablen in Python. Gibt es sie überhaupt?

class ABC: 
    x = 6 

einige Werte einstellen, zunächst für die abc-Instanz, die später für die statische Variable:

abc = ABC() 
abc.x = 2 
ABC.x = 5 

und dann die Ergebnisse drucken:

print abc.x 
print ABC.x 

welche druckt

2 
5 

Jetzt verstehe ich nicht wirklich, was passiert, denn wenn ich in der Klassendefinition x = 6 für "pass" ersetze, wird es nur das selbe ausgeben. Meine Frage ist, was ist der Zweck der Definition einer Variablen in der Klassendefinition in Python, wenn es so aussieht, als ob jemand zu jeder Zeit irgendeine Variable ohne dies einstellen könnte?

Kennt Python auch den Unterschied zwischen Instanz- und statischen Variablen? Nach dem, was ich sah, würde ich es sagen.

+2

Ich glaube, ein Teil der Grund für Ihre Verwirrung ist, dass alle Variablen in der Klasse ABC (und der Instanz abc) "öffentlich" sind. Sie werden auch dynamisch erstellt. so "abc.x = 2" erstellt tatsächlich eine neue nicht statische Elementvariable in der Instanz abc ... Das heißt ... "abc.x = 2" würde funktionieren, wenn es keine statische Variable x gäbe. Das könnte klarer sein, wenn Sie abc.y = 2 ausprobieren. – Tom

+2

@Tom: Bitte posten Sie Ihre Antwort als Antwort, nicht als Kommentar. Es ist sehr verwirrend, Antworten als Kommentare zu sehen. –

Antwort

11

Es gibt Tugend bei der Deklaration einer Klassenbereichsvariable: Sie dient als Standard für einen. Stellen Sie sich eine klassenweite Variable vor, wie Sie es von "statischen" Variablen in einigen anderen Sprachen halten würden.

3

Eine Variable auf Klassenebene (in anderen Sprachen "static" genannt) gehört der Klasse und wird von allen Instanzen der Klasse gemeinsam genutzt.

Eine Instanzvariable ist Teil jeder einzelnen Instanz der Klasse.

Jedoch.

Sie können jederzeit eine neue Instanzvariable hinzufügen.

Um abc.x zu bekommen, muss zuerst nach einer Instanzvariable gesucht werden. Wenn keine Instanzvariable vorhanden ist, versucht sie die Klassenvariable.

Und Einstellung abc.x erstellt (oder ersetzt) ​​eine Instanzvariable.

2

Python unterscheidet zwischen den beiden. Der Zweck könnte mehrere sein, aber ein Beispiel ist dies:

class token(object): 
    id = 0 

    def __init__(self, value): 
     self.value = value 
     self.id = token.id 
     token.id += 1 

Hier wird die Klassenvariable token.id automatisch bei jeder neuen Instanz erhöht, und diese Instanz eine eindeutige ID zur gleichen Zeit in Anspruch nehmen kann, die gestellt werden in self.id. Beide sind an verschiedenen Orten gespeichert - im Klassenobjekt oder im Instanzobjekt können Sie dies in einigen OO-Sprachen wie C++ oder C# mit statischen und Instanzvariablen vergleichen.

In diesem Beispiel, wenn Sie tun:

print token.id 

Sie die nächste ID sehen zugewiesen werden, während:

x = token(10) 
print x.id 

wird die ID dieser Instanz geben.

Jeder kann auch andere Attribute in einer Instanz oder in einer Klasse platzieren, das ist richtig, aber das wäre nicht interessant, da der Klassencode nicht dazu gedacht ist, sie zu verwenden. Das Interesse mit einem Beispiel wie oben ist, dass der Klassencode sie verwendet.

3

Jedes Objekt hat eine __dict__. Die Klasse ABC und seine Instanz, abc, sind beide Objekte, und so jeder hat seine eigene separate __dict__:

In [3]: class ABC: 
    ...:  x=6 

Hinweis ABC.__dict__ hat ein 'x' Schlüssel:

In [4]: ABC.__dict__ 
Out[4]: {'__doc__': None, '__module__': '__main__', 'x': 6} 

In [5]: abc=ABC() 

In [6]: abc.__dict__ 
Out[6]: {} 

Beachten Sie, dass, wenn ‚x 'ist nicht in abc.__dict__, dann die __dict__' s der abc's superclass (es) wird gesucht. So wird abc.x "geerbt" von ABC:

In [14]: abc.x 
Out[14]: 6 

Aber wenn wir setzen abc.x dann sind wir zu ändern abc.__dict__, nicht ABC.__dict__:

In [7]: abc.x = 2 

In [8]: abc.__dict__ 
Out[8]: {'x': 2} 

In [9]: ABC.__dict__ 
Out[9]: {'__doc__': None, '__module__': '__main__', 'x': 6} 

Natürlich können wir ABC.__dict__ ändern, wenn wir wollen:

In [10]: ABC.x = 5 

In [11]: ABC.__dict__ 
Out[11]: {'__doc__': None, '__module__': '__main__', 'x': 5} 
16

Warnung: das folgende ist eine zu starke Vereinfachung; Ich ignoriere __new__() und eine Reihe anderer spezieller Klassenmethoden, und viele Details handwaving. Aber diese Erklärung wird dich in Python ziemlich weit bringen.

Wenn Sie eine Instanz einer Klasse in Python zu erstellen, wie der Aufruf ABC() in Ihrem Beispiel:

abc = ABC() 

Python erstellt ein neues leeres Objekt und setzt seine Klasse zu ABC. Dann ruft es die __init__() an, wenn es eine gibt. Schließlich gibt es das Objekt zurück.

Wenn Sie nach einem Attribut eines Objekts fragen, sucht es zuerst in der Instanz. Wenn es es nicht findet, sucht es in der Klasse der Instanz. Dann in der Basisklasse (es) und so weiter. Wenn es niemanden mit dem definierten Attribut findet, löst es eine Ausnahme aus.

Wenn Sie einem Attribut eines Objekts zuweisen, erstellt es dieses Attribut, wenn das Objekt noch keins hat. Dann setzt es das Attribut auf diesen Wert. Wenn das Objekt bereits über ein Attribut mit diesem Namen verfügt, wird der Verweis auf den alten Wert gelöscht und ein Verweis auf den neuen Wert verwendet.

Diese Regeln machen das beobachtete Verhalten einfach vorhersehbar. Nach dieser Zeile:

abc = ABC() 

nur die ABC Objekt (die Klasse) hat ein Attribut x benannt. Die abc Instanz hat keine eigene x noch, also, wenn Sie nach einem fragen, werden Sie den Wert ABC.x bekommen. Aber dann weisen Sie das Attribut x sowohl der Klasse als auch dem Objekt zu.Und wenn Sie anschließend diese Attribute untersuchen, beobachten Sie, dass die dort angegebenen Werte immer noch da sind.

Jetzt sollten Sie in der Lage sein, vorherzusagen, was der Code:

class ABC: 
    x = 6 

a = ABC() 
ABC.xyz = 5 
print(ABC.xyz, a.xyz) 

Ja: er druckt zwei Fünfer. Möglicherweise haben Sie erwartet, dass eine AttributeError-Ausnahme ausgelöst wird. Aber Python findet das Attribut in der Klasse - obwohl es hinzugefügt wurde nach die Instanz wurde erstellt.

Dieses Verhalten kann Sie wirklich in Schwierigkeiten bringen. Ein klassischer Anfängerfehler in Python:

class ABC: 
    x = [] 

a = ABC() 
a.x.append(1) 

b = ABC() 
print(b.x) 

Die [1] gedruckt wird. Alle Instanzen von ABC() teilen dieselbe Liste. Was Sie wahrscheinlich wollten, war das:

class ABC: 
    def __init__(self): 
    self.x = [] 

a = ABC() 
a.x.append(1) 

b = ABC() 
print(b.x) 

Das wird eine leere Liste wie erwartet drucken.

Ihre genaue Fragen zu beantworten:

Meine Frage ist, was ist der Zweck eine Variable in der Klassendefinition in Python zu definieren, wenn es scheint, wie ich jemand jederzeit eine beliebige Variable, ohne dabei festlegen ?

Ich nehme an, das bedeutet "warum sollte ich Mitglieder innerhalb der Klasse zuweisen, anstatt innerhalb der __init__ Methode?"

Aus praktischen Gründen bedeutet dies, dass die Instanzen keine eigene Kopie des Attributs haben (oder zumindest noch nicht). Dies bedeutet, dass die Instanzen kleiner sind; Es bedeutet auch, dass der Zugriff auf das Attribut langsamer ist. Es bedeutet auch, dass die Instanzen alle denselben Wert für dieses Attribut haben, was bei veränderbaren Objekten der Fall sein kann oder nicht. Schließlich bedeuten Zuweisungen hier, dass der Wert ein Attribut der Klasse ist, und das ist der einfachste Weg, Attribute für die Klasse festzulegen.

Als rein stilistische Angelegenheit ist es kürzerer Code, da Sie nicht alle diese Instanzen von selbst haben. überall. Darüber hinaus macht es keinen großen Unterschied. Die Zuweisung von Attributen in der __init__-Methode stellt jedoch sicher, dass sie eindeutig Instanzvariablen sind.

Ich bin nicht sehr konsequent selbst. Das einzige, was ich sicher tun werde, ist die Zuordnung aller veränderbaren Objekte, die ich nicht teilen möchte in der __init__ Methode.

Kennt Python auch den Unterschied zwischen Instanz- und statischen Variablen? Nach dem, was ich sah, würde ich es sagen.

Python-Klassen haben keine statischen Klassenvariablen wie C++. Es gibt nur Attribute: Attribute des Klassenobjekts und Attribute des Instanzobjekts. Und wenn Sie nach einem Attribut fragen, und die Instanz es nicht hat, erhalten Sie das Attribut von der Klasse.

Die nächste Annäherung einer Klasse statischen Variable in Python wäre ein verstecktes Modul Attribut sein, etwa so:

_x = 3 
class ABC: 
    def method(self): 
    global _x 
    # ... 

Es ist nicht Teil der Klasse per se.Aber das ist eine übliche Python-Redewendung.

0

Der Vorteil eines "statischen" oder in Python ein "Klassenattribut" ist, dass jede Instanz der Klasse Zugriff auf das gleiche Klassenattribut hat. Dies gilt nicht für Instanzattribute, wie Sie vielleicht wissen.

Nehmen Sie zum Beispiel:

class A(object): 
    b = 1 

A.b  # => 1 
inst = A() 
inst2 = A() 
inst.b  # => 1 
inst2.b # => 1 
A.b = 5 
inst.b  # => 5 
inst2.b # => 5 

Wie Sie die Instanz der Klasse Zugriff auf die Klasse Attribut sehen können, die durch die Angabe der Klassenname und dann die Klasse Attribut gesetzt werden kann.

Der schwierige Teil ist, wenn Sie ein Klassenattribut und ein Instanzattribut dasselbe benannt haben. Dies erfordert ein Verständnis von dem, was unter der Haube passiert.

inst.__dict__ # => {} 
A.__dict__  # => {..., 'b': 5} 

Beachten Sie, wie die Instanz nicht b als Attribut hat? Oben, als wir inst.b aufgerufen haben, prüft Python tatsächlich inst.__dict__ für das Attribut, wenn es nicht gefunden wird, dann sucht es A.__dict__ (die Attribute der Klasse). Wenn Python in den Klassenattributen b nachschlägt, wird es natürlich gefunden und zurückgegeben.

Sie können eine verwirrende Ausgabe erhalten, wenn Sie dann ein Instanzattribut mit demselben Namen festlegen. Zum Beispiel:

inst.b = 10 
inst.__dict__ #=> {'b': 10} 
A.b   # => 5 
inst.b  # => 10 

Sie können die Instanz der Klasse sehen, dass nun das Attribut b Instanz hat und deshalb gibt Python diesen Wert.

Verwandte Themen