Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
modul:m323:learningunits:lu02:byreferenceinclasses [2025/11/13 10:31] – kmaurizimodul:m323:learningunits:lu02:byreferenceinclasses [2026/08/25 09:14] (aktuell) – Korrektur mutable Defaults (Code warf ValueError) + Animation eingebunden admin
Zeile 4: Zeile 4:
  
 In Python werden Objekte, einschließlich Dataclasses, immer ''By Reference'' übergeben. Das bedeutet, dass bei der Übergabe eines Objekts an eine Funktion eine Referenz auf das Objekt übergeben wird und keine Kopie davon erstellt wird. Änderungen, die innerhalb der Funktion an den Attributen des Objekts vorgenommen werden, wirken sich direkt auf das ursprüngliche Objekt aus. In Python werden Objekte, einschließlich Dataclasses, immer ''By Reference'' übergeben. Das bedeutet, dass bei der Übergabe eines Objekts an eine Funktion eine Referenz auf das Objekt übergeben wird und keine Kopie davon erstellt wird. Änderungen, die innerhalb der Funktion an den Attributen des Objekts vorgenommen werden, wirken sich direkt auf das ursprüngliche Objekt aus.
 +
 +===== Animation: Objekte, frozen und Standardwerte =====
 +
 +Die Animation zeigt drei Abläufe Schritt für Schritt. Tab 1 und 2 gehören zu den Beispielen weiter unten; Tab 3 zeigt, was bei einem veränderbaren Standardwert passiert – und damit den Grund, weshalb Dataclasses solche Standardwerte gar nicht erst zulassen.
 +
 +{{url>https://templates-python.github.io/m323_html/lu02/objekte-und-standardwerte.html 100%,800px noborder|Animation: Objekte, Dataclasses und Standardwerte}}
  
 ===== Dataclasses ===== ===== Dataclasses =====
Zeile 55: Zeile 61:
 Python-Dataclasses können sowohl mutable als auch immutable Attribute enthalten. Unabhängig davon, ob die Attribute mutable oder immutable sind, wird die Dataclass selbst immer ''By Reference'' übergeben. Python-Dataclasses können sowohl mutable als auch immutable Attribute enthalten. Unabhängig davon, ob die Attribute mutable oder immutable sind, wird die Dataclass selbst immer ''By Reference'' übergeben.
  
-==== Problem: Mutable Default Values ====+==== Mutable Standardwerte sind nicht erlaubt ====
  
-Ein häufiges Problem bei der Verwendung von mutable Objekten (wie Listen) in Dataclasses ist, dass alle Instanzen der Dataclass denselben Standardwert teilen können, wenn dieser direkt als Default-Wert gesetzt wird. Dies kann zu unerwartetem Verhalten führen.+Es liegt nahe, einem Listen-Attribut einfach eine leere Liste als Standardwert zu geben:
  
 <code python> <code python>
Zeile 65: Zeile 71:
 class Student: class Student:
     name: str     name: str
-    grades: list = []+    grades: list = []   # ValueError! 
 +</code>
  
-# Problem: Alle Instanzen teilen sich dieselbe Liste. +Dieser Code lässt sich **nicht ausführen**. Bereits beim Definieren der Klasse bricht Python ab:
-if __name__ == '__main__': +
-    student1 = Student(name='Alice') +
-    student2 = Student(name='Bob')+
  
-    student1.grades.append(90) +<code> 
-    print(student2.grades)  # Output: [90] - Dies ist wahrscheinlich nicht das gewünschte Verhalten+ValueError: mutable default <class 'list'> for field grades is not allowed: use default_factory
 </code> </code>
  
-In diesem Beispiel teilen sich alle Instanzen der Dataclass ''Student'' die gleiche Liste ''grades'', da der Standardwert direkt gesetzt wurde. Wenn eine Note zu einer Instanz hinzugefügt wird, beeinflusst dies auch alle anderen Instanzen.+Der Grund: Ein Standardwert wird **einmalig** ausgewertet – nämlich dann, wenn die Klasse bzw. die Funktion definiert wird, und nicht bei jeder Instanzierung. Alle Instanzen würden sich also dieselbe Liste teilen. Weil das fast nie gewollt ist, verbieten Dataclasses solche Standardwerte von vornherein. 
 + 
 +<WRAP center round important 90%> 
 +**Bei normalen Funktionen greift dieser Schutz nicht!** Dort ist derselbe Fehler erlaubt – und still: 
 + 
 +<code python> 
 +def add_grade(grade, grades=[]): 
 +    grades.append(grade) 
 +    return grades 
 + 
 +print(add_grade(90))  # Output: [90] 
 +print(add_grade(80))  # Output: [90, 80]  <- und nicht [80] 
 +</code> 
 + 
 +Die Liste im Standardwert überlebt den Funktionsaufruf, weil sie nur einmal erzeugt wurde. Tab 3 der Animation oben spielt genau das durch. Die Lösung bei Funktionen: ''None'' als Standardwert verwenden und die Liste im Funktionsrumpf erzeugen. 
 +</WRAP>
  
 ==== Lösung: Verwendung von `field(default_factory=list)` ==== ==== Lösung: Verwendung von `field(default_factory=list)` ====
  
-Um dieses Problem zu lösen, verwendet man in Dataclasses für mutable Standardwerte wie Listen die Funktion `field` mit dem Parameter `default_factory`. Dadurch wird sichergestellt, dass jede Instanz ihre eigene Liste erhält.+Für mutable Standardwerte verwendet man in Dataclasses die Funktion ''field'' mit dem Parameter ''default_factory''. Diese Factory wird bei **jeder** Instanzierung neu aufgerufen – dadurch erhält jede Instanz ihre eigene Liste.
  
 <code python> <code python>
Zeile 97: Zeile 116:
     print(student2.grades)  # Output: [] - Jede Instanz hat ihre eigene, unabhängige Liste     print(student2.grades)  # Output: [] - Jede Instanz hat ihre eigene, unabhängige Liste
 </code> </code>
- 
-Durch die Verwendung von `field(default_factory=list)` wird sichergestellt, dass jede Instanz von ''Student'' eine eigene Liste erhält, und Änderungen an einer Instanz wirken sich nicht auf andere Instanzen aus. 
  
 ===== Immutable Dataclasses ===== ===== Immutable Dataclasses =====
Zeile 115: Zeile 132:
 </code> </code>
  
-In diesem Fall ist die Dataclass ''ImmutablePerson'' unveränderlich, was bedeutet, dass jedes Attribut, sobald es gesetzt ist, nicht mehr geändert werden kann. Jeder Versuch, das Attribut ''age'' oder ''name'' zu ändern, führt zu einem Fehler.+In diesem Fall ist die Dataclass ''ImmutablePerson'' unveränderlich, was bedeutet, dass jedes Attribut, sobald es gesetzt ist, nicht mehr geändert werden kann. Jeder Versuch, das Attribut ''age'' oder ''name'' zu ändern, führt zu einem ''FrozenInstanceError'': 
 + 
 +<code> 
 +FrozenInstanceError: cannot assign to field 'age' 
 +</code>
  
 ==== Beispiel: Anwendung einer frozen Dataclass ==== ==== Beispiel: Anwendung einer frozen Dataclass ====
Zeile 135: Zeile 156:
 if __name__ == '__main__': if __name__ == '__main__':
     p = Person(name='Alice', age=30)     p = Person(name='Alice', age=30)
-    print(f'Vor der Änderung: {p}')  # Output: Vor der Änderung: Person(name='Alice', age=30) 
     new_p = birthday(p)     new_p = birthday(p)
-    print(f'Nach der Änderung: {p}')  # Output: Nach der Änderung: Person(name='Alice', age=30)+    print(f'Original: {p}')  # Output: Original: Person(name='Alice', age=30)
     print(f'Neue Instanz: {new_p}')  # Output: Neue Instanz: Person(name='Alice', age=31)     print(f'Neue Instanz: {new_p}')  # Output: Neue Instanz: Person(name='Alice', age=31)
 </code> </code>
Zeile 156: Zeile 176:
 ===== Zusammenfassung ===== ===== Zusammenfassung =====
  
-In Python werden Objekte, einschließlich Dataclasses, immer ''By Reference'' übergeben. Änderungen an den Attributen eines Objekts innerhalb einer Funktion wirken sich direkt auf das Originalobjekt aus. Um ungewollte Änderungen zu vermeiden, kann eine Dataclass als ''frozen'' deklariert werden, was sie unveränderlich macht und vor unbeabsichtigten Modifikationen schützt. Zusätzlich ist es wichtig, bei der Verwendung von mutable Default-Werten, wie Listen, darauf zu achten, dass jede Instanz ihre eigene unabhängige Kopie erhält, indem `field(default_factory=list)` verwendet wird.+In Python werden Objekte, einschließlich Dataclasses, immer ''By Reference'' übergeben. Änderungen an den Attributen eines Objekts innerhalb einer Funktion wirken sich direkt auf das Originalobjekt aus. Um ungewollte Änderungen zu vermeiden, kann eine Dataclass als ''frozen'' deklariert werden, was sie unveränderlich macht und vor unbeabsichtigten Modifikationen schützt. Zusätzlich ist es wichtig, bei mutable Attributen wie Listen ''field(default_factory=list)'' zu verwenden – ein direkter mutable Standardwert wird von Dataclasses gar nicht erst akzeptiert.
  
 <WRAP center round tip 60%> <WRAP center round tip 60%>
-Tipp: Wenn Sie mutable Attribute in einer Dataclass verwenden, sollten Sie sorgfältig überlegen, ob die direkte Veränderung dieser Attribute innerhalb von Funktionen in Ihrem Programmdesign erwünscht ist. Verwenden Sie ''frozen'' Dataclasses oder `field(default_factory=list)`, um unbeabsichtigte Änderungen zu verhindern.+Tipp: Wenn Sie mutable Attribute in einer Dataclass verwenden, sollten Sie sorgfältig überlegen, ob die direkte Veränderung dieser Attribute innerhalb von Funktionen in Ihrem Programmdesign erwünscht ist. Verwenden Sie ''frozen'' Dataclasses oder ''field(default_factory=list)'', um unbeabsichtigte Änderungen zu verhindern.
 </WRAP> </WRAP>
  
 ---- ----
-{{tag>M323-LU02 M323-AE1}}+{{tag>M323-LU02 M323-A1I M323-A1B}}
 [[https://creativecommons.org/licenses/by-nc-sa/4.0/ch/|{{https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png}}]] (c) Kevin Maurizi  [[https://creativecommons.org/licenses/by-nc-sa/4.0/ch/|{{https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png}}]] (c) Kevin Maurizi 
  
  • modul/m323/learningunits/lu02/byreferenceinclasses.1763026301.txt.gz
  • Zuletzt geändert: 2025/11/13 10:31
  • von kmaurizi