ProgrammierungPython-Entwickler / Data Engineer

Was passiert, wenn ein veränderbares Objekt (z. B. eine Liste oder ein Wörterbuch) an eine Funktion in Python übergeben wird? Wie kann man unerwartete Änderungen innerhalb und außerhalb der Funktion vermeiden?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

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:

  • Die Übergabe eines veränderbaren Objekts ermöglicht es, dessen Zustand sowohl innerhalb als auch außerhalb der Funktion zu ändern.
  • Um dies zu vermeiden, verwendet man Datentransfer (shallow/deep copy).
  • Unveränderliche Objekte sind vor solchen Änderungen geschützt.

Trick Fragen.

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]

Typische Fehler und Anti-Muster

  • Veränderung des übergebenen veränderbaren Objekts innerhalb der Funktion ohne Benachrichtigung des Benutzers.
  • Anwendung von flachem Kopieren bei verschachtelten Datenstrukturen (Fehler mit mutierenden inneren Objekten).
  • Verwendung von veränderbaren Objekten als Standardwerte in Funktionsparametern.

Lebensbeispiel

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.