Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.
| Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung Nächste Überarbeitung | Vorherige Überarbeitung | ||
| talit:tutorial_oop3 [2025-05-26 05:10] – [Speichern & Lesen] hof | talit: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: | ||
| + | * Instanz vs. Klasse | ||
| + | * Methode? | ||
| + | * Warum das ganze? | ||
| + | * Abhilfe: weniger ist mehr | ||
| + | * Nur zwei Klassen: `WordPair`, `VocabularyUnit` | ||
| + | * Stats in WordPair integrieren | ||
| + | * Kerninhalte: | ||
| + | * LearningStrategy, | ||
| + | ++++ | ||
| + | </ | ||
| 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` |
| - | * Methoden und Attribute in `lower_case_with_underscores()` | + | * Methoden und Attribute in `lower_case_with_underscores()` |
| * Dokumentation mit `""" | * Dokumentation mit `""" | ||
| * Der doc-string für die ganze Klasse beschreibt deren Funktion. Typischerweise ist der erste Satz die Vervollständigung des Satzanfangs "Ein < | * Der doc-string für die ganze Klasse beschreibt deren Funktion. Typischerweise ist der erste Satz die Vervollständigung des Satzanfangs "Ein < | ||
| * 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 < | * 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 < | ||
| - | |||
| ### 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)`, | + | Als drittes benötigen wir eine Klasse `ConsoleLearner` mit einer Methode `learn(unit)`, |
| * um das Terminal zu leeren kannst du `print(" | * um das Terminal zu leeren kannst du `print(" | ||
| * um nur die aktuelle Zeile im Terminal zu löschen, verwendest du `print(" | * um nur die aktuelle Zeile im Terminal zu löschen, verwendest du `print(" | ||
| Zeile 121: | Zeile 134: | ||
| ## Objekte und Klassen in Python | ## Objekte und Klassen in Python | ||
| - | < | ||
| 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: | ||
| - | < | + | < |
| print(f' | print(f' | ||
| - | print(f'" | + | print(f'" |
| Sogar Funktionen sind Objekte: | Sogar Funktionen sind Objekte: | ||
| - | < | + | < |
| ### Vererbung | ### Vererbung | ||
| Zeile 139: | Zeile 151: | ||
| Wir können die Vererbung explizit angeben: | Wir können die Vererbung explizit angeben: | ||
| - | < | + | < |
| def __init__(self, | def __init__(self, | ||
| self.name = name # Attribute shared between all objects. | self.name = name # Attribute shared between all objects. | ||
| Zeile 157: | Zeile 169: | ||
| for animal in [Cat(" | for animal in [Cat(" | ||
| - | animal.make_sound()</ | + | animal.make_sound()</ |
| | | ||
| Wenn nicht anders angegeben, erbt eine Klasse direkt von der `object` Klasse. Diese definiert [[https:// | Wenn nicht anders angegeben, erbt eine Klasse direkt von der `object` Klasse. Diese definiert [[https:// | ||
| - | < | + | < |
| pair = Animal() | pair = Animal() | ||
| - | print(pair)</ | + | print(pair)</ |
| Ü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: | ||
| - | < | + | < |
| def __init__(self, | def __init__(self, | ||
| self.name = name # Attribute shared between all objects. | self.name = name # Attribute shared between all objects. | ||
| Zeile 181: | Zeile 193: | ||
| for animal in [Animal(" | for animal in [Animal(" | ||
| - | print(animal)</ | + | print(animal)</ |
| Zeile 189: | Zeile 200: | ||
| Wir möchten aufzeichnen, | Wir möchten aufzeichnen, | ||
| - | 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 |
| 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) |
| $$\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' | ||
| + | |||
| + | 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> | ||
| + | |||
| Zeile 334: | Zeile 351: | ||
| ] | ] | ||
| </ | </ | ||
| - | |||
| ### Aufgabe E: JSON-Serialisierung | ### Aufgabe E: JSON-Serialisierung | ||
| Zeile 345: | Zeile 361: | ||
| import json | import json | ||
| with open(filename, | with open(filename, | ||
| - | json.dump(json_list, | + | json.dump(json_list, |
| </ | </ | ||
| + | |||
| + | |||
| ### 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) | ||
| </ | </ | ||
| - | |||
| ### 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: | ||
| </ | </ | ||
| + | |||
| + | ### Aufgabe G - Webapp | ||
| + | S. auch [[talit: | ||