Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.

Link zu der Vergleichsansicht

Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung
Nächste Überarbeitung
Vorherige Überarbeitung
talit:tutorial_oop3 [2025-05-26 05:13] – [Aufgabe E: JSON-Serialisierung] hoftalit:tutorial_oop3 [2026-06-01 15:25] (aktuell) hof
Zeile 1: Zeile 1:
 # Objekt-orientierte Programmierung # Objekt-orientierte Programmierung
 +
 +<nodisp 2>
 +++++ Notizen für 2027|
 +  * Feedback sca / Beobachtung: es bleibt wenig OO hängen:
 +    * Instanz vs. Klasse
 +    * Methode?
 +    * Warum das ganze?
 +  * Abhilfe: weniger ist mehr
 +    * Nur zwei Klassen: `WordPair`, `VocabularyUnit`
 +    * Stats in WordPair integrieren
 +    * Kerninhalte: WordPair, Unit, Learning function (ohne OO), Serialisierung
 +    * LearningStrategy, StopCriterion als Additum für Highflyers behalten.
 +++++
 +</nodisp>
  
 Code mit vielen Eigenschaften kann schnell unübersichtlich werden - sogar selbst geschriebener Code altert schlecht und ist nach wenigen Wochen unlesbar. Was macht der folgende Code? Code mit vielen Eigenschaften kann schnell unübersichtlich werden - sogar selbst geschriebener Code altert schlecht und ist nach wenigen Wochen unlesbar. Was macht der folgende Code?
Zeile 99: Zeile 113:
 Objekte werden manchmal auch als _Instanzen_ (einer Klasse) bezeichnet; im obigen Code-Beispiel ist das in der Variable `tree` gespeicherte Objekt eine Instanz der Klasse `WordPair`. Objekte werden manchmal auch als _Instanzen_ (einer Klasse) bezeichnet; im obigen Code-Beispiel ist das in der Variable `tree` gespeicherte Objekt eine Instanz der Klasse `WordPair`.
 #### Coding Conventions #### Coding Conventions
-  * Klassennamen in `CamelCase` +  * Klassennamen in `CamelCase` ([[wp>Camel_Case]]) 
-  * Methoden und Attribute in `lower_case_with_underscores()`+  * Methoden und Attribute in `lower_case_with_underscores()` ([[wp>Snake_case]])
   * Dokumentation mit `"""doc strings"""`   * Dokumentation mit `"""doc strings"""`
     * Der doc-string für die ganze Klasse beschreibt deren Funktion. Typischerweise ist der erste Satz die Vervollständigung des Satzanfangs "Ein <classname> ist ...". Im obigen Beispiel "[A WordPair is] a word and its translation.".     * Der doc-string für die ganze Klasse beschreibt deren Funktion. Typischerweise ist der erste Satz die Vervollständigung des Satzanfangs "Ein <classname> ist ...". Im obigen Beispiel "[A WordPair is] a word and its translation.".
     * Der doc-string für Methoden beschreibt die Tätigkeit der Methode. Typischerweise beginnt der Satz mit einem Verb und ist die Vervollständigung des Satzanfangs "Die <functionname> Methode ...". Im obigen Beispiel "[The reversed method] returns a copy of the pair in the reverse direction."     * Der doc-string für Methoden beschreibt die Tätigkeit der Methode. Typischerweise beginnt der Satz mit einem Verb und ist die Vervollständigung des Satzanfangs "Die <functionname> Methode ...". Im obigen Beispiel "[The reversed method] returns a copy of the pair in the reverse direction."
- 
  
 ### Aufgabe A ### Aufgabe A
Zeile 115: Zeile 128:
 Wir möchten zusätzlich eine Klasse `VocabularyUnit` haben, die eine Liste von `WordPair`s speichert. Wie sieht die `__init__` Methode dieser Klasse aus? Wir möchten zusätzlich eine Klasse `VocabularyUnit` haben, die eine Liste von `WordPair`s speichert. Wie sieht die `__init__` Methode dieser Klasse aus?
  
-Als drittes benötigen wir eine Klasse `ConsoleLearner` mit einer Methode `learn(unit)`, die alle Wort-Paare abfragt.+Als drittes benötigen wir eine Klasse `ConsoleLearner` mit einer Methode `learn(unit)`, die alle Wort-Paare einer `VocabularyUnit` abfragt.
   * um das Terminal zu leeren kannst du `print("\033c", end="")` benützen   * um das Terminal zu leeren kannst du `print("\033c", end="")` benützen
   * um nur die aktuelle Zeile im Terminal zu löschen, verwendest du `print("\033[H\033[J", end="")`   * um nur die aktuelle Zeile im Terminal zu löschen, verwendest du `print("\033[H\033[J", end="")`
Zeile 121: Zeile 134:
  
 ## Objekte und Klassen in Python ## Objekte und Klassen in Python
-<html><script type="module" src="https://bottom.ch/ksr/ed/bottom-editor.js"></script></html> 
  
 Jeder Wert in Python ist eigentlich ein Objekt. Zahlen sind Objekte der Klassen `int` oder `float`. Die Klasse eines Objekts kann mit der Funktion `type()` herausgefunden werden: Jeder Wert in Python ist eigentlich ein Objekt. Zahlen sind Objekte der Klassen `int` oder `float`. Die Klasse eines Objekts kann mit der Funktion `type()` herausgefunden werden:
  
-<html><bottom-editor>print(f'42 is of class {type(42)}')+<bottom-editor>print(f'42 is of class {type(42)}')
 print(f'3.14 is of class {type(3.14)}') print(f'3.14 is of class {type(3.14)}')
-print(f'"foobar" is of class {type("foobar")}')</bottom-editor></html>+print(f'"foobar" is of class {type("foobar")}')</bottom-editor>
  
 Sogar Funktionen sind Objekte: Sogar Funktionen sind Objekte:
  
-<html><bottom-editor>print(f'len() is of class {type(len)}')</bottom-editor></html>+<bottom-editor>print(f'len() is of class {type(len)}')</bottom-editor>
  
 ### Vererbung ### Vererbung
Zeile 139: Zeile 151:
 Wir können die Vererbung explizit angeben: Wir können die Vererbung explizit angeben:
  
-<html><bottom-editor>class Animal:+<bottom-editor>class Animal:
     def __init__(self, name):     def __init__(self, name):
         self.name = name   # Attribute shared between all objects.         self.name = name   # Attribute shared between all objects.
Zeile 157: Zeile 169:
  
 for animal in [Cat("Tom"), Dog("Spike")]: for animal in [Cat("Tom"), Dog("Spike")]:
-    animal.make_sound()</bottom-editor></html>+    animal.make_sound()</bottom-editor>
          
 Wenn nicht anders angegeben, erbt eine Klasse direkt von der `object` Klasse. Diese definiert [[https://docs.python.org/3/reference/datamodel.html#basic-customization|allerhand praktische Methoden]], die wir aber _überschreiben_ können. Beispielsweise definiert die `__str__` Methode, wie ein Objekt in einen String umgewandelt wird. Dies wird von `print` benützt und gibt im Allgemeinen die Klasse und die Speicheradresse des Objekts aus: Wenn nicht anders angegeben, erbt eine Klasse direkt von der `object` Klasse. Diese definiert [[https://docs.python.org/3/reference/datamodel.html#basic-customization|allerhand praktische Methoden]], die wir aber _überschreiben_ können. Beispielsweise definiert die `__str__` Methode, wie ein Objekt in einen String umgewandelt wird. Dies wird von `print` benützt und gibt im Allgemeinen die Klasse und die Speicheradresse des Objekts aus:
  
-<html><bottom-editor>class Animal: pass+<bottom-editor>class Animal: pass
 pair = Animal() pair = Animal()
-print(pair)</bottom-editor></html>+print(pair)</bottom-editor>
  
 Überschreiben wir die `__str__` Methode, können wir bestimmen, wie Objekte in Strings umgewandelt werden: Überschreiben wir die `__str__` Methode, können wir bestimmen, wie Objekte in Strings umgewandelt werden:
  
-<html><bottom-editor>class Animal:+<bottom-editor>class Animal:
     def __init__(self, name):     def __init__(self, name):
         self.name = name   # Attribute shared between all objects.         self.name = name   # Attribute shared between all objects.
Zeile 181: Zeile 193:
  
 for animal in [Animal("Unknown"), Cat("Tom"), Dog("Spike")]: for animal in [Animal("Unknown"), Cat("Tom"), Dog("Spike")]:
-    print(animal)</bottom-editor></html> +    print(animal)</bottom-editor>
  
  
Zeile 189: Zeile 200:
 Wir möchten aufzeichnen, wie oft unsere Wörter richtig bzw. falsch übersetzt werden. Dafür benötigen wir eine `Stats`-Klasse. Neben den Attributen `correct` bzw. `incorrect` möchten wir ein Attribut `score` haben, das einen Wert zwischen `[0..1]` bereitstellt, wobei `0` bedeutet, dass das Wort noch komplett unbekannt ist, und `1`, dass das Wort perfekt gelernt wurde. Wir möchten aufzeichnen, wie oft unsere Wörter richtig bzw. falsch übersetzt werden. Dafür benötigen wir eine `Stats`-Klasse. Neben den Attributen `correct` bzw. `incorrect` möchten wir ein Attribut `score` haben, das einen Wert zwischen `[0..1]` bereitstellt, wobei `0` bedeutet, dass das Wort noch komplett unbekannt ist, und `1`, dass das Wort perfekt gelernt wurde.
  
-Die Klasse `Stats` soll eine Methode `record(correct)` erhalten: Jedes Mal, wenn ein Wortpaar getestet wird, findet der Learner heraus, ob das Wort richtig oder falsch ist. Dieses Verdikt wird mit `record()` an die Statistik weitergeleitet und dort aufgezeichnet.+Die Klasse `Stats` soll eine Methode `record(outcome)` erhalten: Jedes Mal, wenn ein Wortpaar getestet wird, findet der Learner heraus, ob das Wort richtig oder falsch ist. Dieses Verdikt oder _Outcome_ (`True` oder `False`) wird mit `record()` an die Statistik weitergeleitet und dort aufgezeichnet.
  
 Zudem sollte `Stats` auch noch eine sinnvolle `__str__` Methode haben. Zudem sollte `Stats` auch noch eine sinnvolle `__str__` Methode haben.
Zeile 203: Zeile 214:
   * Der neueste Versuch soll mehr Gewicht haben als lange zurückliegende Versuche.   * Der neueste Versuch soll mehr Gewicht haben als lange zurückliegende Versuche.
  
-Es bietet sich an, mit einem Decay zu arbeiten: jedes Mal, wenn ein neuer Wert dazukommt, wird der alte Score mit einem Faktor <1 multipliziert. Mit einem Faktor von 0.5 setzt sich der Score zur Hälfte aus dem neuesten Test, zur anderen Hälfte aus dem bisherigen Score zusammen: +Es bietet sich an, mit einem _Decay_ (_de_: Zerfall) zu arbeiten: jedes Mal, wenn ein neuer Wert dazukommt, wird der alte Score mit einem Faktor $\lambda < 1multipliziert, der neue Wert wird mit dem Faktor $1-\lambda$ dazu addiert. Mit einem Faktor von 0.5 setzt sich der Score zur Hälfte aus dem neuesten Test, zur anderen Hälfte aus dem bisherigen Score zusammen: 
 $$\begin{aligned} score_{new} &= 0.5 \cdot (test_0 + score_{old}) \\ $$\begin{aligned} score_{new} &= 0.5 \cdot (test_0 + score_{old}) \\
   &= 0.5 \cdot (test_0 + 0.5 \cdot (test_1 + 0.5 \cdot (test_2 + \ldots))) \\   &= 0.5 \cdot (test_0 + 0.5 \cdot (test_1 + 0.5 \cdot (test_2 + \ldots))) \\
   &= \frac{test_0}{2} + \frac{test_1}{4} + \frac{test_2}{8} + \frac{test_3}{16} + \ldots \end{aligned}$$   &= \frac{test_0}{2} + \frac{test_1}{4} + \frac{test_2}{8} + \frac{test_3}{16} + \ldots \end{aligned}$$
 +  
 +
 +##### Wilson's Plus Four
 +
 +Unser _Score_ ist mit dem Zerfall genau genommen keine Erfolgsquote mehr. Was wir eigentlich möchten: Berechnen, wie gross die Wahrscheinlichkeit ist, dass wir ein Wortpaar falsch beantworten. Genau können wir sie nicht wissen, aber wir können die Untergrenze des wahrscheinlichen Intervalls aufgrund der bisherigen Antworten abschätzen. [[wpde>Konfidenzintervall_f%C3%BCr_die_Erfolgswahrscheinlichkeit_der_Binomialverteilung|Wikipedia]] hat ein paar Ideen dazu, die [[wp>Plus_Four_Rule]] in der englischen WP zeigt einen ganz einfachen Weg: wir starten mit 2 korrekten und 2 inkorrekten Antworten (2/4 richtigen).
 +
  
  
Zeile 344: Zeile 361:
         import json         import json
         with open(filename, 'w') as out:         with open(filename, 'w') as out:
-            json.dump(json_list, out, indent=2)  # indent=2 alignts the output nicely +            json.dump(json_list, out, indent=2)  # indent=2 aligns the output nicely 
-</code +</code> 
->+ 
 ### Statische Methoden ### Statische Methoden
 Fürs Einlesen kommt die umgekehrte `json.load` Funktion zum Einsatz. Allerdings haben wir noch ein kleines Problem: Eine VocabularyUnit existiert ja noch gar nicht, wenn wir sie einlesen wollen aus der Datei. Wir benötigen also eine Funktion, die nicht an eine bestimmte Unit gebunden ist. Diese werden mit `@staticmethod` annotiert und haben keinen `self` Parameter. Statische Funktionen werden direkt über den Klassennamen aufgerufen. Fürs Einlesen kommt die umgekehrte `json.load` Funktion zum Einsatz. Allerdings haben wir noch ein kleines Problem: Eine VocabularyUnit existiert ja noch gar nicht, wenn wir sie einlesen wollen aus der Datei. Wir benötigen also eine Funktion, die nicht an eine bestimmte Unit gebunden ist. Diese werden mit `@staticmethod` annotiert und haben keinen `self` Parameter. Statische Funktionen werden direkt über den Klassennamen aufgerufen.
Zeile 374: Zeile 392:
             return VocabularyUnit(pairs)             return VocabularyUnit(pairs)
 </code> </code>
- 
 ### Aufgabe F ### Aufgabe F
 Füge statische Methoden zu `VocabularyUnit` und `WordPair` hinzu, um die gespeicherten Daten wieder einlesen zu können. Füge statische Methoden zu `VocabularyUnit` und `WordPair` hinzu, um die gespeicherten Daten wieder einlesen zu können.
Zeile 394: Zeile 411:
 </code> </code>
  
 +
 +### Aufgabe G - Webapp
 +S. auch [[talit:flask_webserver]] und [[ef_informatik:webapps:start]].
  • talit/tutorial_oop3.1748236416.txt.gz
  • Zuletzt geändert: 2025-05-26 05:13
  • von hof