Wie sieht das Problem aus?
Stellen Sie sich vor, Sie schreiben eine Funktion, um Aufgaben zu einer Aufgabenliste hinzuzufügen:
def add_task(task, todo_list=[]):
todo_list.append(task)
return todo_list
# Wir fügen die erste Aufgabe hinzuprint(add_task("Milch kaufen")) # ['Milch kaufen']gen die zweite Aufgabe hinzuprint(add_task("Mit dem Hund spazieren gehen")) # Erwartet: ['Mit dem Hund spazieren gehen'] # Wir erhalten: ['Milch kaufen', 'Mit dem Hund spazieren gehen']⚠️ Was ist passiert? Wir hatten erwartet, eine neue Liste mit einer Aufgabe zu erhalten, aber die zweite Aufgabe wurde zur ersten hinzugefügt! Und wir haben die vorherige Liste nirgendwo gespeichert. Magie? Nein, das sind veränderliche Standardargumente.
Warum passiert das?
Wenn Python eine Funktion erstellt, erstellt es ein Standardobjekt einmal zum Zeitpunkt der Funktionsdefinition und nicht bei jedem Aufruf. Dieses Objekt wird gespeichert und bei jedem nachfolgenden Aufruf wiederverwendet.
Mal sehen, was wirklich passiert:
def add_task(task, todo_list=[]):
todo_list.append(task)
print(f"List ID: {id(todo_list)}")
return todo_list
add_task("First task") # Listen-ID: 140234567891234add_task("Zweite Aufgabe") # Listen-ID: 140234567891234 (die gleiche!)add_task("Dritte Aufgabe") # Listen-ID: 140234567891234 (und wieder die gleiche!)💡 Der entscheidende Punkt: Jedes Mal wird dasselbe Listenobjekt verwendet. Alle Funktionsaufrufe arbeiten mit derselben Liste im Speicher, sodass alle Änderungen kumuliert werden.
Welche Datentypen sind betroffen?
Dieses Problem betrifft alle veränderlichen Datentypen in Python. Dazu gehören Listen, Wörterbücher, Sets und benutzerdefinierte Objekte. Aber Zahlen, Strings und Tuples (unveränderliche Typen) funktionieren normal, weil sie nicht geändert werden können, man kann nur neue erstellen.
# Problemcode mit Wörterbuchdef add_user(name, users_dict={}):
users_dict[name] = True
return users_dict
print(add_user("Alexey")) # {'Alexey': True}print(add_user("Maria")) # {'Alexey': True, 'Maria': True} - beide sind hier!# Sicherer Code mit der Zeile (immutable)def greet(name, greeting="Hallo"): greeting = greeting + ", " + name
return greeting
print(greet("Ivan")) # Hallo, Ivanprint(greet("Olga")) # Hallo Olga - alles funktioniert wie erwartetDie richtige Entscheidung.
Die klassische Lösung für dieses Problem besteht darin, None als Standardwert zu verwenden und dann ein neues Objekt innerhalb der Funktion zu erstellen:
def add_task(task, todo_list=None):
if todo_list is None:
todo_list = []
todo_list.append(task)
return todo_list
# Jetzt funktioniert alles richtigprint(add_task("Milch kaufen")) # ['Milch kaufen']print(add_task("Mit dem Hund spazieren gehen")) # ['Mit dem Hund spazieren gehen']✅ Regel: Jedes Mal, wenn wir die Liste nicht übergeben, wird eine neue leere Liste erstellt. Problem gelöst!
Das gleiche funktioniert für Wörterbücher:
def create_profile(name, data=None):
if data is None:
data = {}
data['name'] = name
data['created_at'] = 'today'
return data
profile1 = create_profile("Alexey")
profile2 = create_profile("Maria")
print(profile1) # {'name': 'Alex', 'created_at': 'heute'}print(profile2) # {'name': 'Maria', 'created_at': 'heute'}
Wann kann das nützlich sein?
Interessanterweise kann dieses Verhalten manchmal speziell verwendet werden, zum Beispiel zum Zwischenspeichern von Ergebnissen:
def get_config(config_cache={}):
if 'data' not in config_cache:
print("Loading configuration...")
config_cache['data'] = "important settings"
return config_cache['data']
print(get_config()) # Konfiguration wird geladen... wichtige Einstellungenprint(get_config()) # wichtige Einstellungen (nicht mehr geladen)💡 Hinweis: Aber im echten Code ist es besser, spezielle Tools wie functools.lru_cache für das Caching zu verwenden, um den Code verständlicher zu machen.
Wie kann man diesen Fehler vermeiden?
Denken Sie einfach an eine einfache Regel: Verwenden Sie niemals veränderbare Objekte (Listen, Wörterbücher, Sets) als Standardwerte in Funktionsargumenten. Verwenden Sie immer None und erstellen Sie das gewünschte Objekt innerhalb der Funktion.
Dieser Fehler ist so häufig, dass viele Linter (Code-Überprüfungstools) speziell davor warnen. Wenn Sie PyCharm, VSCode mit pylint oder flake8 verwenden, wird eine Warnung angezeigt, wenn Sie versuchen, einen solchen Code zu schreiben.
Lernen Sie Python richtig beim ersten Mal!
Dieses und viele andere wichtige Themen können im Detail studiert werden in Kodike - alle Nuancen mit klaren Erklärungen zu analysieren und in der Praxis mit interessanten Aufgaben zu festigen. Jedes Thema wird von Beispielen aus der realen Entwicklung begleitet, und praktische Aufgaben helfen, das erworbene Wissen sofort anzuwenden.
Und wenn Sie während des Trainings Fragen haben oder komplexe Punkte besprechen möchten, haben wir bereits mehr 2000 Gleichgesinnte im aktiven Telegram-Kanal, wo sie immer helfen, den Weg in die Programmierung zu verstehen und zu unterstützen!
