Wie vermeiden Sie, dass classndaten zwischen Instanzen ausgetauscht werden?

Was ich will, ist dieses Verhalten:

class a: list = [] x = a() y = a() x.list.append(1) y.list.append(2) x.list.append(3) y.list.append(4) print(x.list) # prints [1, 3] print(y.list) # prints [2, 4] 

Was wirklich passiert, wenn ich drucke, ist natürlich:

 print(x.list) # prints [1, 2, 3, 4] print(y.list) # prints [1, 2, 3, 4] 

Offensichtlich teilen sie die Daten in class a . Wie bekomme ich separate Instanzen, um das gewünschte Verhalten zu erreichen?

   

Du willst das:

 class a: def __init__(self): self.list = [] 

Durch das Deklarieren der Variablen in der classndeklaration werden sie zu “classn” -Mitgliedern und nicht zu Instanz-Mitgliedern. Wenn Sie sie in der __init__ Methode __init__ , stellen Sie sicher, dass eine neue Instanz der Member neben jeder neuen Instanz des Objekts erstellt wird. __init__ ist das __init__ Verhalten.

Sie haben “list” als “Eigenschaft auf classnebene” und nicht als “Eigenschaft auf Instanzebene” deklariert. Damit Eigenschaften auf Instanzebene definiert werden können, müssen Sie sie durch Referenzieren mit dem Parameter “self” in der __init__ Methode (oder anderswo abhängig von der Situation) initialisieren.

Sie müssen die Instanzeigenschaften in der Methode __init__ nicht unbedingt initialisieren, aber sie erleichtert das Verständnis.

Die angenommene Antwort funktioniert, aber ein wenig mehr Erklärung tut nicht weh.

classnattribute werden beim Erstellen einer Instanz nicht zu Instanzattributen. Sie werden Instanzattribute, wenn ihnen ein Wert zugewiesen wird.

Im ursprünglichen Code wird dem Listenattribut nach der Instanziierung kein Wert zugewiesen. Es bleibt also ein classnattribut. Die Definition der Liste in __init__ funktioniert, weil __init__ nach der Instanziierung __init__ wird. Alternativ würde dieser Code auch die gewünschte Ausgabe erzeugen:

 >>> class a: list = [] >>> y = a() >>> x = a() >>> x.list = [] >>> y.list = [] >>> x.list.append(1) >>> y.list.append(2) >>> x.list.append(3) >>> y.list.append(4) >>> print(x.list) [1, 3] >>> print(y.list) [2, 4] 

Das verwirrende Szenario in der Frage wird jedoch niemals unveränderlichen Objekten wie Zahlen und Zeichenfolgen passieren, da ihr Wert ohne Zuweisung nicht geändert werden kann. Zum Beispiel funktioniert ein Code ähnlich dem Original mit String-Attributtyp ohne Probleme:

 >>> class a: string = '' >>> x = a() >>> y = a() >>> x.string += 'x' >>> y.string += 'y' >>> x.string 'x' >>> y.string 'y' 

Also zusammenfassend: classnattribute werden genau dann __init__ wenn ihnen nach der Instanziierung ein Wert zugewiesen wird, der in der Methode __init__ oder nicht . Dies ist eine gute Sache, da Sie auf diese Weise statische Attribute haben können, wenn Sie einem Attribut nach der Instanziierung niemals einen Wert zuweisen.

Obwohl die akzeptierte Antwort genau richtig ist, würde ich gerne eine kleine Beschreibung hinzufügen.

Lass uns eine kleine Übung machen

Definieren Sie zunächst eine class wie folgt:

 class A: temp='Skyharbor' def __init__(self, x): self.x=x def change(self, y): self.temp=y 

Was haben wir hier?

  • Wir haben eine sehr einfache class, die ein Attribut temp das eine Zeichenkette ist
  • Eine Init- Methode, die self.x
  • Eine Änderungsmethode setzt self.temp

Ziemlich einfach so weit, ja? Jetzt fangen wir an, mit dieser class herumzuspielen. Lassen Sie uns zuerst diese class initialisieren:

 a = A('Tesseract') 

Jetzt mache folgendes:

 >>> print a.temp Skyharbor >>> print A.temp Skyharbor 

Nun, a.temp arbeitete wie erwartet, aber wie zur Hölle arbeitete A.temp ? Nun, es hat funktioniert, weil Temp ein classnattribut ist. Alles in Python ist ein Objekt. Hier ist A auch ein Objekt des classntyps. Daher ist das Attribut temp ein Attribut der class A, und wenn Sie den Wert von temp durch A (und nicht durch eine Instanz von a) ändern, wird der geänderte Wert in allen Instanzen der class A wiedergegeben. Lass uns voran gehen und das tun:

 >>> A.temp = 'Monuments' >>> print A.temp Monuments >>> print a.temp Monuments 

Interessant ist es nicht? Und beachte, dass id (a.temp) und id (A.temp) immer noch gleich sind

Jedes Python-Objekt erhält automatisch ein dict- Attribut, das seine Liste von Attributen enthält. Lassen Sie uns untersuchen, was dieses Wörterbuch für unsere Beispielobjekte enthält:

 >>> print A.__dict__ { 'change': , '__module__': '__main__', '__init__': , 'temp': 'Monuments', '__doc__': None } >>> print a.__dict__ {x: 'Tesseract'} 

Beachten Sie, dass das Attribut ” temp unter den Attributen einer class aufgelistet ist, während “x” für die Instanz aufgelistet ist

Wie kommt es, dass wir einen definierten Wert von a.temp wenn er nicht einmal für die Instanz a aufgelistet ist a.temp Nun, das ist die Magie der Methode __getattribute__() . In Python wird diese Methode automatisch von der a.temp Syntax a.temp Wenn wir also a.temp schreiben, führt Python a aus. getattribute (‘temp’). Diese Methode führt die Attributsuche durch, dh sie sucht den Wert des Attributs, indem sie an verschiedenen Stellen sucht.

Die Standardimplementierung von __getattribute__() durchsucht zuerst das interne Dictionary ( dict ) eines Objekts und dann den Typ des Objekts selbst. In diesem Fall führt a.__getattribute__('temp') zuerst a.__dict__['temp'] und dann a.__class__.__dict__['temp']

Okay, jetzt benutzen wir unsere change Methode:

 >>> a.change('Intervals') >>> print a.temp Intervals >>> print A.temp Monuments 

Nun, da wir selbst verwendet haben, gibt uns print a.temp einen anderen Wert als print A.temp .

Wenn wir nun id (a.temp) und id (A.temp) vergleichen, werden sie verschieden sein

Ja, Sie müssen im “Konstruktor” deklarieren, wenn Sie möchten, dass die Liste eine Objekteigenschaft und keine classneigenschaft wird.

Daher scheint fast jede Antwort hier einen bestimmten Punkt zu verfehlen. classnvariablen werden nie zu Instanzvariablen, wie der folgende Code zeigt. Durch Verwendung einer Metaklasse zum Abfangen der Variablenzuweisung auf classnebene können wir sehen, dass bei der Neuzuweisung von a.myattr die magische Methode der Feldzuweisung für die class nicht aufgerufen wird. Dies liegt daran, dass die Zuweisung eine neue Instanzvariable erstellt . Dieses Verhalten hat absolut nichts mit der classnvariablen zu tun, wie die zweite class zeigt, die keine classnvariablen hat und dennoch die Feldzuweisung erlaubt.

 class mymeta(type): def __init__(cls, name, bases, d): pass def __setattr__(cls, attr, value): print("setting " + attr) super(mymeta, cls).__setattr__(attr, value) class myclass(object): __metaclass__ = mymeta myattr = [] a = myclass() a.myattr = [] #NOTHING IS PRINTED myclass.myattr = [5] #change is printed here b = myclass() print(b.myattr) #pass through lookup on the base class class expando(object): pass a = expando() a.random = 5 #no class variable required print(a.random) #but it still works 

IN SHORT classnvariablen haben nichts mit Instanzvariablen zu tun.

Deutlicher Sie befinden sich zufällig im Bereich für Nachschlagevorgänge in Instanzen. classnvariablen sind tatsächlich Instanzvariablen für das classnobjekt selbst. Sie können auch Metaklassenvariablen verwenden, wenn Sie auch möchten, da Metaklassen selbst auch Objekte sind. Alles ist ein Objekt, ob es verwendet wird, um andere Objekte zu erstellen oder nicht, also werden Sie nicht in die Semantik anderer Sprachen eingebunden, wenn Sie die Wortklasse verwenden. In Python ist eine class in Wirklichkeit nur ein Objekt, mit dem bestimmt wird, wie andere Objekte erstellt werden und wie sich ihr Verhalten verhält. Metaklassen sind classn, die classn erstellen, um diesen Punkt weiter zu veranschaulichen.

Um Ihre von einer anderen Instanz freigegebene Variable zu schützen, müssen Sie bei jedem Erstellen einer Instanz eine neue Instanzvariable erstellen. Wenn Sie eine Variable innerhalb einer class deklarieren, ist diese classnvariable und wird von allen Instanzen geteilt. Wenn Sie es zum Beispiel sinnvoll machen möchten, müssen Sie die init- Methode verwenden, um die Variable als Verweis auf die Instanz neu zu initialisieren

Aus Python-Objekten und class von Programiz.com :

__init__() function. Diese spezielle function wird immer dann aufgerufen, wenn ein neues Objekt dieser class instanziiert wird.

Diese Art von function wird in der objektorientierten Programmierung (Object Oriented Programming, OOP) auch als Konstruktor bezeichnet. Wir verwenden es normalerweise, um alle Variablen zu initialisieren.

Beispielsweise:

 class example: list=[] #This is class variable shared by all instance def __init__(self): self.list = [] #This is instance variable referred to specific instance 

Ich denke, die Antworten sind irreführend. Eine in einer class definierte Eigenschaft wird zu einer Instanzeigenschaft, wenn das Objekt instanziiert wird, unabhängig davon, wie Sie es definieren . Es werden also Kopien von a.list erstellt, und x.list und y.list sind verschiedene Kopien. Der Grund dafür, dass sie identisch zu sein scheinen, ist, dass sie beide Aliase für dieselbe Liste sind. Aber das ist eine Konsequenz der functionsweise von Listen und nicht der Art, wie classn funktionieren. Wenn Sie dasselbe mit Nummern anstelle von Listen tun würden (oder einfach + = anstelle von append verwenden, was eine neue Liste erstellen würde), würden Sie sehen, dass das Ändern von x.attr keinen Einfluss auf das Ändern von y.attr .

Das Definieren von self.list in __init__ funktioniert, weil die function zweimal aufgerufen wird, einmal für jedes Mal, wenn das Objekt instanziiert wird, und so werden zwei verschiedene Listen erstellt.