Allgemeine Frage zum hinzufügen zu Liste in Klasse

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Annie
User
Beiträge: 2
Registriert: Sonntag 2. Januar 2011, 15:34

Hi ihr alle,
das ist mein erster Post, ich hoffe, dass das mit dem Code highlighten funktioniert, ich bitte um Umsicht ;-)
Des weiteren arbeite ich noch nicht sehr lange mit Python, wie ihr vielleicht gleich merken werdet.
Ich bin vorhin auf ein Problem gestoßen, als ich etwas an eine Liste in einer selbst definierten Klasse anhängen wollte. Das ganze sah im Kerne so aus:

Code: Alles auswählen

class TestClass:
    x = None
    lst = []

inst1 = TestClass()
inst2 = TestClass()

inst1.x = 200
inst1.lst.append(1)
inst1.lst.append(2)

inst2.x = 300
inst2.lst.append(3)
inst2.lst.append(4)

print(inst1.x, inst1.lst)
print(inst2.x, inst2.lst)
Die Print Funktion gibt jetzt "200 [1, 2, 3, 4]" und "300 [1, 2, 3, 4]" zurück, das heißt die beiden x in den Instanzen sind wie erwartet unabhängig voneinander, die beiden Listen sind jedoch irgendwie die selben, ich hatte erwartet dass in einer [1, 2] und in der andern [3, 4] steht.
Das ganze lässt sich beheben, wenn ich die Klasse anders definiere:

Code: Alles auswählen

class TestClass:
    def __init__(self):
        self.x = None
        self.lst = []
Sollten Attribute immer im __init__ Teil einer Klasse definiert werden? Ich war bisher davon ausgegangen, der __init__ Teil wäre eher dazu da Parameter beim erstellen einer Instanz zu verarbeiten. Ich verstehe nicht so recht, weshalb im oberen Beispiel die Listen in zwei verschiedenen Instanzen der Klasse scheinbar Referenzen voneinander sind und sich wie eine einzige verhalten, die beiden x aber unabhängig voneinander sind. Jegliche Form der Aufklärung würde ich sehr begrüßen...

Liebe Grüße, Annie

Edit: highlighting von code auf code=python geändert
Zuletzt geändert von Annie am Sonntag 2. Januar 2011, 16:32, insgesamt 1-mal geändert.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Willkommen im Forum!
Zum highlighten: Es gibt auch Python-Highlighting mit dem [ python ] Tag.

`__init__` ist dazu da Exemplare zu initialisieren. Ja, auch um die Parameter zu verarbeiten aber nicht nur. In dem Fall ist es noetig, weil die Exemplarattribute willst, aber hier Klassenattribute nutzt, das heisst _alle_ Exemplare von `TestClass` haben _dieselbe_ Liste.

Warum die `x` verschieden sind ist einfach: Du erstellst ein neues Instanzattribut. Fuer `lst` waere `inst1.lst = [].append(1)` aequivalent, aber _nicht_ `inst1.lst.append(1)`.
`inst1.__class__.x` hat immernoch `None`.
Und `inst1.__class__.lst is inst1.lst` evaluiert zu True.
BlackJack

@Annie: Das `lst` setzt Du *einmal* auf eine leere Liste und dann veränderst Du nur immer wieder diese Liste. Wenn ein Attribut beim Abfragen nicht auf dem Exemplar vorhanden ist, dann wird es in der Klasse gesucht. `inst1.lst` und `inst2.lst` sind deshalb die selbe Liste, denn weder `inst1` noch `inst2` hat jemals ein `lst`-Attribut. Bei dem `x` weist Du aber explizit beiden Exemplaren ein eigenes `x`-Attribut zu.

Attribute die auf den Exemplaren unabhängig voneinander existieren sollen, musst Du in der `__init__` binden. In der Klasse geht das nicht, weil dort die Exemplare ja noch gar nicht existieren. In der Klasse solltest Du nur Namen an Werte binden, die auch tatsächlich auf der Klasse existieren sollen. Und das sollten in der Regel keine veränderbaren Werte, sondern Konstanten sein, denn diese Werte werden von allen Exemplaren der Klasse geteilt.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Im ersten Fall definierst du die Attribute auf der Klasse, im zweiten Fall auf der Instanz. Wie du erkannt hast willst du i.d.R. letzteres da sich aufgrund der Weise wie Attribute auf Instanzen ermittelt werden, die Instanzen sich die Klassenattribute teilen.

Klassenattribute nutzt man hauptsächlich für deklarative APIs oder Optionen die man per Vererbung verändern können soll.
Annie
User
Beiträge: 2
Registriert: Sonntag 2. Januar 2011, 15:34

Vielen Dank für die schnellen und aufschlussreichen Antworten, das hat echt geholfen.
Ich wäre niemals drauf gekommen, dass inst1.x = 200 ein neues Instanzattribut einführt, ich war davon ausgegengen, dass ich einen Error bekomme, wenn ich ein Attribut schreiben möchte, dass in der Klasse nicht definiert ist. Hab das aber grade ausprobiert, selbst inst1.y = 250 gibt keine Fehlermeldung, ist ja interessant, dass das dann einfach als neues Instanzattribut angelegt wird.
Hab den Unterschied zwischen Klassenattributen und Instanzattributen jetzt aber verstanden, auch wenn das auf den ersten Blick nicht so intuitiv erscheint, da man ja von außen betrachtet echt denkt, man hätte jetzt was überschrieben, dabei hat man was neues erzeugt. Lohnt sich wohl hin und wieder mal Variablen auf Identität zu überprüfen...

Nochmals vielen Dank für das Beantworten dieser grundlegenden Sache, hab leider keinen großen Informatik Background und verwende Python als Skriptsprache in einem CAD Programm, das seither nur VBscript kannte. Hab mich dann aber mal in Python reingelesen und gemerkt, dass es viel mehr Möglichkeiten bietet und einige nervige Dinge sehr vereinfacht. Der Umstieg hat sich echt gelohnt, aber am Anfang gibts halt immer fiese Stolpersteine ;-)

Liebe Grüße, Annie
Antworten