Gibt es einen sinnvollen Unterschied zwischen:
class A(object):
foo = 5 # some default value
vs.
class B(object):
def __init__(self, foo=5):
self.foo = foo
Wenn Sie viele Instanzen erstellen, gibt es Unterschiede in der Leistung oder im Platzbedarf für die beiden Stile? Betrachten Sie beim Lesen des Codes die Bedeutung der beiden Stile als erheblich unterschiedlich?
python
attributes
member-variables
Dan Homerick
quelle
quelle
Antworten:
Über Leistungsüberlegungen hinaus gibt es einen signifikanten semantischen Unterschied. Im Fall eines Klassenattributs wird nur auf ein Objekt verwiesen. In der Instanzattributmenge, die bei der Instanziierung festgelegt wird, können mehrere Objekte referenziert werden. Zum Beispiel
quelle
int
undstr
sie hängen immer noch mit jeder Instanz und nicht mit der Klasse zusammen.int
undstr
werden auch genauso geteilt. Sie können dies einfach mitis
oder überprüfenid
. Oder schauen Sie einfach in die einzelnen Instanzen__dict__
und Klassen__dict__
. Es spielt normalerweise keine Rolle, ob unveränderliche Typen gemeinsam genutzt werden oder nicht.a.foo = 5
, dass in beiden Fällen eineb.foo
Rückkehr angezeigt wird[]
. Dies liegt daran, dass Sie im ersten Fall das Klassenattributa.foo
mit einem neuen Instanzattribut mit demselben Namen überschreiben .Der Unterschied besteht darin, dass das Attribut der Klasse von allen Instanzen gemeinsam genutzt wird. Das Attribut einer Instanz ist für diese Instanz eindeutig.
Wenn sie aus C ++ stammen, ähneln die Attribute der Klasse eher statischen Elementvariablen.
quelle
>>> class A(object): foo = 5
>>> a, b = A(), A()
>>> a.foo = 10
>>> b.foo
5
a.foo.append(5)
den Wert mutiert , diea.foo
sich bezieht, währenda.foo = 5
machta.foo
für den Wert in einen neuen Namen5
. Sie erhalten also ein Instanzattribut, das das Klassenattribut verbirgt. Versuchen Sie dasselbea.foo = 5
in Alex 'Version und Sie werden sehen, dass diesb.foo
unverändert ist.Hier ist ein sehr guter Beitrag und fasse ihn wie folgt zusammen.
Und in visueller Form
Zuweisung von Klassenattributen
Wenn ein Klassenattribut durch Zugriff auf die Klasse festgelegt wird, wird der Wert für alle Instanzen überschrieben
Wenn eine Klassenvariable durch Zugriff auf eine Instanz festgelegt wird, wird der Wert nur für diese Instanz überschrieben . Dies überschreibt im Wesentlichen die Klassenvariable und verwandelt sie in eine Instanzvariable, die intuitiv nur für diese Instanz verfügbar ist .
Wann würden Sie das Klassenattribut verwenden?
Konstanten speichern . Da auf Klassenattribute als Attribute der Klasse selbst zugegriffen werden kann, ist es oft hilfreich, sie zum Speichern von klassenweiten, klassenspezifischen Konstanten zu verwenden
Standardwerte definieren . Als triviales Beispiel können wir eine begrenzte Liste erstellen (dh eine Liste, die nur eine bestimmte Anzahl von Elementen oder weniger enthalten kann) und eine Standardobergrenze von 10 Elementen festlegen
quelle
Da die Leute in den Kommentaren hier und in zwei anderen Fragen, die als Dups markiert sind, alle auf die gleiche Weise verwirrt zu sein scheinen, denke ich, dass es sich lohnt, zusätzlich zu Alex Coventrys eine zusätzliche Antwort hinzuzufügen .
Die Tatsache, dass Alex einen Wert eines veränderlichen Typs wie eine Liste zuweist, hat nichts damit zu tun, ob Dinge geteilt werden oder nicht. Wir können dies mit der
id
Funktion oder demis
Operator sehen:(Wenn Sie sich fragen, warum ich
object()
anstelle von beispielsweise verwendet habe,5
um zu vermeiden, dass ich auf zwei ganz andere Probleme stoße, auf die ich hier nicht eingehen möchte. Aus zwei verschiedenen Gründen können vollständig separat erstellte5
s die sein gleiche Instanz der Nummer5
. Aber völlig separat erstellteobject()
s können nicht.)Warum
a.foo.append(5)
wirkt sich das in Alex 'Beispiel ausb.foo
,a.foo = 5
in meinem Beispiel jedoch nicht? Nun, versuchena.foo = 5
in Alex Beispiel, und bemerkt , dass es nicht wirkt sichb.foo
dort entweder .a.foo = 5
macht nura.foo
einen Namen für5
. Dies wirkt sich nicht aufb.foo
einen anderen Namen für den alten Wert aus, auf dena.foo
früher Bezug genommen wurde. * Es ist etwas schwierig, ein Instanzattribut zu erstellen, das ein Klassenattribut verbirgt. ** Sobald Sie das erhalten, ist nichts kompliziertes passiert hier.Hoffentlich ist jetzt klar, warum Alex eine Liste verwendet hat: Die Tatsache, dass Sie eine Liste mutieren können, bedeutet, dass es einfacher ist zu zeigen, dass zwei Variablen dieselbe Liste benennen, und dass es im realen Code wichtiger ist, zu wissen, ob Sie zwei Listen haben oder zwei Namen für dieselbe Liste.
* Die Verwirrung für Leute, die aus einer Sprache wie C ++ kommen, ist, dass in Python Werte nicht in Variablen gespeichert werden. Werte leben im Werteland von sich aus, Variablen sind nur Namen für Werte, und die Zuweisung erstellt nur einen neuen Namen für einen Wert. Wenn es hilft, stellen Sie sich jede Python-Variable als eine
shared_ptr<T>
anstelle von a vorT
.** Einige Benutzer nutzen dies, indem sie ein Klassenattribut als "Standardwert" für ein Instanzattribut verwenden, das von Instanzen festgelegt werden kann oder nicht. Dies kann in einigen Fällen nützlich sein, kann aber auch verwirrend sein. Seien Sie also vorsichtig damit.
quelle
Es gibt noch eine Situation.
Klassen- und Instanzattribute sind Deskriptor .
Oben wird ausgegeben:
Dieselbe Art von Instanzzugriff über Klasse oder Instanz liefert unterschiedliche Ergebnisse!
Und ich fand in c.PyObject_GenericGetAttr Definition, und einen tollen Beitrag .
Erklären
quelle