Seite 1 von 1

Liste aus Klasse wird immer überschrieben

Verfasst: Freitag 29. September 2023, 15:10
von Martin6832
Hallo Zusammen,
ich komme bei einem Thema einfach nicht weiter... Und zwar verstehe ich nicht warum meine list1 ab Zeile 14 immer wieder die gleichen Werte anzeigt wie list2. Diesen Effekt sieht man in dem Konsolen Output, nachdem ich der list2 den Wert 2 einfüge.... Ich möchte der Liste2 eigentlich nur einmalig die Werte der Liste1 übergeben und nicht das beide Listen ab Zeile 14 immer die gleichen Werte haben.

Wenn ich die Listen in dem Beispiel Code durch Variablen ersetze, dann wird der Wert in Zeile 14 auch nur einmalig übergeben und beide Variablen sind danach von einander unabhängig...

Kann mir jemand das Phänomen (dass nur bei Listen aufkommt) erklären?

Vielen Dank im Voraus und Grüße,
Martin


Hier der Code zu meinem Problem:

Code: Alles auswählen

list2=[]

class AnyClass:
    list1 = []

    def __init__(self, list1 = ["1"]):
        self.list1 = list1

Instance1 = AnyClass()

print("list1:", Instance1.list1)
print("list2:", list2)

list2 = Instance1.list1

print("list1:", Instance1.list1)
print("list2:", list2)

list2.insert(0,"2")
print("list1:", Instance1.list1)
print("list2:", list2)

console:
list1: ['1']
list2: []
list1: ['1']
list2: ['1']
list1: ['2', '1']
list2: ['2', '1']

Re: Liste aus Klasse wird immer überschrieben

Verfasst: Freitag 29. September 2023, 17:14
von sparrow
Du bindest eine leere Liste an den Namen list2
Dann erstellst du eine zweite Liste (ob die in einer Klasse steckt ist egal) und bindest diese Liste zusätzlich an den Namen list2. Was auch immer vorher an list2 gebunden, ist dann darüber nicht mehr referenziert.

Verkürzt tust du das:

Code: Alles auswählen

bar = []
foo = bar
foo und bar referenzieren die selbe Liste.

Re: Liste aus Klasse wird immer überschrieben

Verfasst: Freitag 29. September 2023, 17:22
von __blackjack__
@Martin6832: Listen durch Variablen ersetzen macht als Aussage keinen Sinn. Du meinst Listen durch unveränderliche Werte wie beispielsweise Zahlen oder Zeichenketten ersetzen.

Nach ``list2 = Instance1.list1`` hast Du zwei Wege auf die *selbe* Liste zuzugreifen. Über `list2` und über `Instance1.list1`. Das wäre bei einer Zahl oder einer Zeichenkette nicht anders. Nur das man Zahlen und Zeichenketten nicht verändern kann. Listen aber schon.

In Python werden bei Zuweisungen und Parameterübergaben immer die Objekte selbst übergeben. Da wird nie ohne Dein zutun eine Kopie erstellt. Womit die Lösung ist: Du musst selbst eine Kopie erstellen wenn Du mit einer Kopie weiterarbeiten möchtest. Und dabei dann auch gleich selbst entscheiden wie weit/tief diese Kopie gehen soll/muss. Also ob beispielsweise ein einfaches ``list2 = list(Instance1.list1)`` ausreicht oder nicht.

Anmerkungen zu zwei problematischen Sachen im Code: Ausdrücke für Defaultwerte werden nur einmal ausgewertet, wenn die ``def``-Anweisung ausgeführt wird. Und *nicht* bei jedem Aufruf der Funktion oder Methode. Darum ist es eine schlechte Idee veränderbare Objekte als Defaultwerte zu setzen. Das ist Zustand der *einmal* initialisiert wird, und den sich dann alle Exemplare dieses Typs teilen. Also letztlich globaler Zustand. Den will man nicht haben. Idiomatischer Umgang damit ist `None` als Defaultwert zu nehmen und in der `__init__()` dann gegebenfalls ein neues Objekt zu erstellen.

Code: Alles auswählen

class AnyClass:
    def __init__(self, list1=None):
        self.list1 = ["1"] if list1 is None else list1
Das andere ist das Klassenattribut `list1`: Auch hier möchte man keine veränderlichen Objekte haben, beziehungsweise welche verändert werden. Klassenattribute die verändert werden sind globaler Zustand und damit ein Problem. Auf Klassen werden deshalb in der Regel nur Konstanten und Methoden definiert.

Re: Liste aus Klasse wird immer überschrieben

Verfasst: Mittwoch 4. Oktober 2023, 09:21
von Martin6832
Danke für Eure Antworten! Das Kopieren mit ``list2 = list(Instance1.list1)`` hat für meinen Anwendungsfall funktioniert.

Ich hatte ansonsten auch die letzten Tage rumgespielt mit Variablen und Listen und mir id() anzeigen lassen wie und was referenziert wird... Jetzt ist es um einiges klarer.

@blackjack: Danke für deinen Hinweis die Listen über den oben beschriebenen Weg mit "None" zu initialisieren, das hat das Problem gelöst, dass alle Objekte auf die gleiche Liste referenziert hatten.

Viele Grüße,
Martin

Re: Liste aus Klasse wird immer überschrieben

Verfasst: Mittwoch 4. Oktober 2023, 10:26
von Sirius3
Eine Kopie einer Liste zu erstellen, um sie anschließend zu verändern ist selten die richtige Lösung.
Gewöhn Dir einfach an, Listen für einen Zweck zu erstellen, und wenn Du sie für einen anderen Zweck brauchst, sie modifiziert neu zu erstellen.

Das sieht jetzt bei dem Dummy-Beispiel nicht so sinnvoll aus, aber vom Prinzip her:

Code: Alles auswählen

class AnyClass:
    def __init__(self, list1=None):
        self.list1 = ["1"] if list1 is None else list1


def main():
    instance1 = AnyClass()
    list2 = []
    print("list1:", instance1.list1)
    print("list2:", list2)

    list2 = Instance1.list1
    print("list1:", instance1.list1)
    print("list2:", list2)

    list2 = ["2"] + list2 # erstellen einer neuen modifizierten Liste
    print("list1:", instance1.list1)
    print("list2:", list2)


if __name__ == "__main__":
    main()

Re: Liste aus Klasse wird immer überschrieben

Verfasst: Mittwoch 4. Oktober 2023, 12:06
von Martin6832
Danke Sirius! Das mit der modifizierten Liste sieht nach gutem Programmier-Stil aus.