Historie der Frage:
Die Übergabe von Parametern in Python folgt dem Prinzip „Call by Object Reference“ (manchmal auch „Call by Sharing“ genannt). Das bedeutet, dass die Variable innerhalb der Funktion auf dasselbe Objekt im Speicher verweist wie das von außen übergebene Argument.
Problem:
Wenn die Funktion das übergebene veränderbare Objekt (z. B. eine Liste oder ein Wörterbuch) ändert, sind die Änderungen auch außerhalb der Funktion sichtbar. Dies kann zu schwer nachvollziehbaren Fehlern führen, insbesondere wenn erwartet wird, dass die Funktion die Eingabedaten nicht verändert.
Lösung:
Um Nebeneffekte zu vermeiden, sollte man innerhalb der Funktion eine Kopie des Objekts anfertigen oder unveränderliche Datenstrukturen verwenden. Zum Kopieren werden Standardmethoden verwendet (z. B. list.copy() für Listen, dict.copy() für Wörterbücher oder copy.deepcopy()).
Beispielcode:
def append_one(xs): xs.append(1) return xs lst = [0] append_one(lst) print(lst) # [0, 1] # Wie vermeidet man Änderungen? Eine Kopie machen: def safe_append_one(xs): ys = xs.copy() ys.append(1) return ys lst2 = [0] safe_append_one(lst2) print(lst2) # [0]
Wichtige Merkmale:
Kann man sicher sein, dass eine Liste, die durch .copy() erstellt wurde, unabhängig von der ursprünglichen Liste ist?
Nein – .copy() erstellt eine flache Kopie. Wenn darin veränderbare Objekte enthalten sind, sind Änderungen an ihnen auch in der Originalversion sichtbar.
import copy lst = [[1, 2], [3, 4]] shallow = lst.copy() shallow[0][0] = 42 print(lst) # [[42, 2], [3, 4]] deep = copy.deepcopy(lst) deep[0][0] = 100 print(lst) # [[42, 2], [3, 4]]
Ist die Rückgabe eines neuen Objekts basierend auf dem Eingangsobjekt eine Garantie dafür, dass das Original nicht verändert wird?
Nicht immer. Wenn innerhalb des neuen Objekts Teile des ursprünglichen Objekts verwendet werden (z. B. ein Verweis auf eine innere verschachtelte Liste), kann das ursprüngliche Objekt geändert werden.
def duplicate_list(xs): return xs * 2 lst = [[1], [2]] res = duplicate_list(lst) res[0][0] = 999 print(lst) # [[999], [2]]
Können Standardparameterwerte für veränderbare Objekte Probleme bei mehrfachen Funktionsaufrufen verursachen?
Ja – der Standardwert wird nur einmal bei der Definition der Funktion berechnet.
def add_item(item, container=[]): container.append(item) return container print(add_item(1)) # [1] print(add_item(2)) # [1, 2]
Negativer Fall
In einer Konfigurationsverarbeitungsbibliothek wurde eine Liste als Standardwert verwendet, was dazu führte, dass Elemente zwischen verschiedenen Funktionsaufrufen angesammelt wurden. Das Verhalten war unvorhersehbar und wurde sehr lange festgestellt.
Vorteile:
Weniger Code für wiederholte Aufrufe, sichtbare Speichereinsparungen.
Nachteile:
Unauffälliges Verhalten, Schwierigkeiten beim Debuggen, langfristige Fehler.
Positiver Fall
Verwendung von None als Standardwert und explizite Erstellung eines neuen Objekts bei jedem Aufruf.
Vorteile:
Vorhersehbarkeit, keine unerwarteten Nebeneffekte, Zuverlässigkeit.
Nachteile:
Erfordert Achtsamkeit und etwas mehr Code.