From fbaec82182bceed96c7189bdb557eed4cf73e440 Mon Sep 17 00:00:00 2001 From: Alex Mueller Date: Sat, 29 Aug 2026 12:53:24 +0200 Subject: [PATCH] =?UTF-8?q?Erweitere=20=C3=9Cbungsreihe=20um=20detailliert?= =?UTF-8?q?e=20Erkl=C3=A4rungen,=20Stolperfallen-Guides=20und=20modernes?= =?UTF-8?q?=20HTML-Lernportal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01_einstieg_klassen/README.md | 35 +- 01_einstieg_klassen/aufgabe.py | 52 ++- 01_einstieg_klassen/index.html | 192 +++++++++ 02_init_und_self/README.md | 14 + 02_init_und_self/aufgabe.py | 51 ++- 02_init_und_self/index.html | 171 ++++++++ 03_methoden_und_verhalten/README.md | 10 + 03_methoden_und_verhalten/aufgabe.py | 79 ++-- 03_methoden_und_verhalten/index.html | 152 ++++++++ 04_str_und_darstellung/README.md | 10 + 04_str_und_darstellung/aufgabe.py | 32 +- 04_str_und_darstellung/index.html | 155 ++++++++ 05_objekte_kombinieren/aufgabe.py | 48 ++- 05_objekte_kombinieren/index.html | 150 ++++++++ 06_abschlussprojekt_tamagotchi/README.md | 2 +- 06_abschlussprojekt_tamagotchi/aufgabe.py | 51 +-- 06_abschlussprojekt_tamagotchi/index.html | 133 +++++++ assets/style.css | 449 ++++++++++++++++++++++ index.html | 166 ++++++++ 19 files changed, 1811 insertions(+), 141 deletions(-) create mode 100644 01_einstieg_klassen/index.html create mode 100644 02_init_und_self/index.html create mode 100644 03_methoden_und_verhalten/index.html create mode 100644 04_str_und_darstellung/index.html create mode 100644 05_objekte_kombinieren/index.html create mode 100644 06_abschlussprojekt_tamagotchi/index.html create mode 100644 assets/style.css create mode 100644 index.html diff --git a/01_einstieg_klassen/README.md b/01_einstieg_klassen/README.md index 4a6cfea..8178b43 100644 --- a/01_einstieg_klassen/README.md +++ b/01_einstieg_klassen/README.md @@ -1,10 +1,10 @@ -# Kapitel 01: Die erste Klasse – Was sind Klassen & Objekte? +# Kapitel 01: Die erste Klasse – Was sind Klassen & Objekte? 🐶🐱 -Herzlich willkommen in der Welt der objektorientierten Programmierung (kurz: **OOP**)! 🐶🐱 +Herzlich willkommen in der Welt der objektorientierten Programmierung (kurz: **OOP**)! Bisher hast du in Python mit Variablen, Listen, Dictionaries und Funktionen gearbeitet. Das hat super funktioniert! Doch wenn Programme größer werden, wird es schnell unübersichtlich. -Hier kommen **Klassen** ins Spiel. +Mit **Klassen** lernst du jetzt, wie du deine eigenen, maßgeschneiderten Datentypen erstellst. --- @@ -43,7 +43,7 @@ Eine einfache Klasse definierst du mit dem Schlüsselwort `class`: ```python class Auto: - pass # 'pass' bedeutet: "Hier passiert erst mal nichts" + pass # 'pass' bedeutet: "Hier passiert erst mal nichts weiter" ``` Aus dieser Klasse kannst du nun ein Objekt erzeugen (**Instanziierung**): @@ -71,16 +71,31 @@ dein_auto.ps = 180 --- -## 🎯 Deine Aufgabe +## ⚠️ Häufige Stolperfallen für Einsteiger -Öffne jetzt die Datei `aufgabe.py` und bearbeite die Aufgaben: -1. Erstelle die Klasse `Haustier`. -2. Erzeuge zwei Haustier-Objekte `bello` und `mimi` mit Eigenschaften. -3. Schreibe zwei kleine Hilfsfunktionen, die mit deinen Haustier-Objekten arbeiten. +1. **Groß- und Kleinschreibung:** + - Klassen werden per Konvention in **CamelCase** mit großem Anfangsbuchstaben geschrieben: `class Haustier:`, `class BankKonto:`. + - Variablen und Funktionen schreibt man klein mit Unterstrichen: `mein_haustier = Haustier()`. +2. **Klammern beim Erstellen nicht vergessen:** + - `bello = Haustier()` erzeugt ein neues Objekt. + - `bello = Haustier` (ohne Klammern) würde die Klasse selbst speichern, kein neues Objekt erzeugen! +3. **`return` in Funktionen nicht vergessen:** + - Wenn deine Funktion ein neu gebautes Haustier erzeugen soll, muss am Ende `return tier` stehen, sonst gibt die Funktion `None` zurück. + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne jetzt die Datei `aufgabe.py` und bearbeite die nummerierten TODOs: +1. **TODO 1:** Definiere die leere Klasse `Haustier`. +2. **TODO 2:** Schreibe die Funktion `erstelle_bello()`. +3. **TODO 3:** Schreibe die Funktion `erstelle_mimi()`. +4. **TODO 4:** Schreibe die Funktion `steckbrief_text(haustier)`. +5. **TODO 5:** Schreibe die Funktion `aelteres_tier(tier1, tier2)`. ### Testen: Führe deine Tests aus mit: ```bash python3 test_aufgabe.py ``` -Sobald alle Tests bestanden sind, kannst du mit Kapitel 02 weitermachen! +Sobald alle Tests mit `OK` durchlaufen, bist du bereit für Kapitel 02! diff --git a/01_einstieg_klassen/aufgabe.py b/01_einstieg_klassen/aufgabe.py index 5b8d88f..e9b7dd9 100644 --- a/01_einstieg_klassen/aufgabe.py +++ b/01_einstieg_klassen/aufgabe.py @@ -3,13 +3,16 @@ Kapitel 01: Die erste Klasse – Aufgabenblatt ============================================ Bearbeite die Aufgaben Schritt für Schritt von TODO 1 bis TODO 5. -Wenn du fertig bist, überprüfe deine Lösung mit: +Wenn du fertig bist, überprüfe deine Lösung im Terminal mit: python3 test_aufgabe.py """ # ============================================================================== # TODO 1: Definiere eine leere Klasse namens "Haustier". -# Tipp: Verwende das Schlüsselwort 'class' und vorerst 'pass' im Klassenrumpf. +# +# Erklärung: +# Eine Klasse ist ein Bauplan. +# Schreibe 'class Haustier:' und rück in der nächsten Zeile 'pass' ein. # ============================================================================== # Schreibe hier deinen Code für TODO 1: @@ -18,13 +21,14 @@ Wenn du fertig bist, überprüfe deine Lösung mit: # ============================================================================== # TODO 2: Erstelle eine Funktion namens "erstelle_bello()". -# Diese Funktion soll: -# 1. Ein neues Haustier-Objekt erzeugen. -# 2. Dem Objekt folgende Attribute zuweisen: -# - name: "Bello" -# - tierart: "Hund" -# - alter: 3 -# 3. Das fertige Objekt zurückgeben (return). +# +# Schritt-für-Schritt-Anleitung: +# 1. Erstelle ein neues Haustier-Objekt: z.B. hund = Haustier() +# 2. Setze die drei Attribute mit dem Punkt-Operator: +# hund.name = "Bello" +# hund.tierart = "Hund" +# hund.alter = 3 +# 3. Gib das erstellte Objekt mit 'return hund' zurück! # ============================================================================== def erstelle_bello(): @@ -34,13 +38,12 @@ def erstelle_bello(): # ============================================================================== # TODO 3: Erstelle eine Funktion namens "erstelle_mimi()". -# Diese Funktion soll: -# 1. Ein neues Haustier-Objekt erzeugen. -# 2. Dem Objekt folgende Attribute zuweisen: -# - name: "Mimi" -# - tierart: "Katze" -# - alter: 5 -# 3. Das fertige Objekt zurückgeben (return). +# +# Ähnlich wie bei Bello: +# - name = "Mimi" +# - tierart = "Katze" +# - alter = 5 +# Vergiss nicht das 'return'! # ============================================================================== def erstelle_mimi(): @@ -50,13 +53,15 @@ def erstelle_mimi(): # ============================================================================== # TODO 4: Schreibe eine Funktion "steckbrief_text(haustier)". +# # Die Funktion bekommt ein Haustier-Objekt als Parameter übergeben. # Sie soll einen formatierten Text im folgenden Format zurückgeben: # "{name} ist ein(e) {tierart} und ist {alter} Jahre alt." # +# Tipp: Nutze einen f-String: f"{haustier.name} ist ein(e) {haustier.tierart} ..." +# # Beispiel: -# Wenn bello übergeben wird, soll zurückgegeben werden: -# "Bello ist ein(e) Hund und ist 3 Jahre alt." +# Wenn bello übergeben wird: "Bello ist ein(e) Hund und ist 3 Jahre alt." # ============================================================================== def steckbrief_text(haustier): @@ -66,9 +71,16 @@ def steckbrief_text(haustier): # ============================================================================== # TODO 5: Schreibe eine Funktion "aelteres_tier(tier1, tier2)". +# # Die Funktion bekommt zwei Haustier-Objekte übergeben. # Sie soll dasjenige Haustier-Objekt zurückgeben, dessen 'alter' größer ist. # Wenn beide Tiere gleich alt sind, soll tier1 zurückgegeben werden. +# +# Tipp: +# if tier2.alter > tier1.alter: +# return tier2 +# else: +# return tier1 # ============================================================================== def aelteres_tier(tier1, tier2): @@ -89,6 +101,6 @@ if __name__ == "__main__": print(steckbrief_text(bello)) print(steckbrief_text(mimi)) aelter = aelteres_tier(bello, mimi) - print(f"Das ältere Tier ist: {aelter.name}") + print(f"Das ältere Tier ist: {aelter.name} ({aelter.alter} Jahre)") else: - print("Hinweis: Implementiere erst die TODOs, um die Ausgabe zu sehen!") + print("💡 Hinweis: Implementiere erst die TODOs, um die Ausgabe zu sehen!") diff --git a/01_einstieg_klassen/index.html b/01_einstieg_klassen/index.html new file mode 100644 index 0000000..8426656 --- /dev/null +++ b/01_einstieg_klassen/index.html @@ -0,0 +1,192 @@ + + + + + + Kapitel 01: Einstieg in Klassen + + + + +
+
+ +
Kapitel 01
+

Die erste Klasse: Bauplan vs. Objekt 🐶

+

Verstehe den Unterschied zwischen einer Klasse und einer Instanz und baue deine ersten Haustier-Objekte.

+
+
+ +
+ + +
+

1. Die wichtigste Idee: Bauplan vs. Haus

+ +

+ In Python ist alles, was du bisher kennengelernt hast (Zahlen, Strings, Listen, Dictionaries), im Grunde ein Objekt. Doch jetzt lernst du, wie du deine ganz eigenen Datentypen erfindest! +

+ +
+
🏗️ Die Plätzchenform-Analogie:
+

Stell dir das Backen vor:

+
    +
  • Die Klasse (class) ist die Ausstechform. Sie legt fest: Jedes Plätzchen hat einen Kopf, zwei Arme und zwei Beine. Die Form selbst kann man aber nicht essen.
  • +
  • Das Objekt (Instanz) ist das echte Plätzchen, das du aus dem Teig ausstichst. Ein Plätzchen bekommt Schokoglasur, das andere Zuckerstreusel. Beide stammen aus derselben Form, sind aber eigenständig!
  • +
+
+ +

Warum nicht einfach Dictionaries?

+

Ein Dictionary hat keine feste Struktur – man vertippt sich leicht:

+ +
+
+
+ + + +
+ Python (Problem mit Dictionaries) +
+
tier1 = {"name": "Bello", "tierart": "Hund", "alter": 3}
+tier2 = {"name": "Mimi", "art": "Katze", "alter": 5}  # Ups! 'art' statt 'tierart'!
+# Ein Programm würde hier abstürzen, weil tier2["tierart"] nicht existiert!
+
+ +

Mit einer Klasse definierst du eine klare, wiederverwendbare Struktur.

+
+ + +
+

2. Wie definiert und nutzt man eine Klasse?

+ +

Eine einfache Klasse erstellst du mit dem Schlüsselwort class:

+ +
+
+
+ + + +
+ Python +
+
# 1. Bauplan definieren:
+class Auto:
+    pass  # pass bedeutet: Hier steht erst mal kein weiterer Code
+
+# 2. Ein Objekt erzeugen (Instanziierung):
+mein_auto = Auto()
+
+# 3. Eigenschaften (Attribute) mit dem Punkt (.) setzen:
+mein_auto.farbe = "Rot"
+mein_auto.marke = "VW"
+mein_auto.ps = 110
+
+# 4. Werte wieder abfragen:
+print(mein_auto.farbe)  # Gibt "Rot" aus
+
+ +
+
⚠️ Häufige Stolperfalle: Klassenname Großschreiben!
+ In Python schreibt man Klassennamen standardmäßig in CamelCase mit großem Anfangsbuchstaben (z.B. Haustier, BankKonto), während Variablen und Funktionen klein geschrieben werden (z.B. mein_haustier, erstelle_tier()). +
+
+ + +
+

3. Deine Aufgaben in aufgabe.py

+ +

Öffne die Datei 01_einstieg_klassen/aufgabe.py und bearbeite die nummerierten TODOs:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ Tipp zu TODO 1 & 2 (Objekt erstellen und befüllen) +
+
+
class Haustier:
+    pass
+
+def erstelle_bello():
+    tier = Haustier()  # Neues Objekt erzeugen
+    tier.name = "Bello"
+    tier.tierart = "Hund"
+    tier.alter = 3
+    return tier  # WICHTIG: Das fertige Objekt zurückgeben!
+
+
+
+ +
+ Tipp zu TODO 5 (Tiere vergleichen) +
+

Du kannst mit einem einfachen if auf die Attribute der übergebenen Objekte zugreifen:

+
+
if tier2.alter > tier1.alter:
+    return tier2
+else:
+    return tier1
+
+
+
+
+ + +
+

4. Lösung überprüfen

+

Führe im Terminal im Ordner 01_einstieg_klassen folgenden Befehl aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/02_init_und_self/README.md b/02_init_und_self/README.md index 6c29963..52082c8 100644 --- a/02_init_und_self/README.md +++ b/02_init_und_self/README.md @@ -77,6 +77,20 @@ k2 = Bankkonto("Ben", 50.0) --- +## ⚠️ Typische Fehler vermeiden + +1. **`__init__` mit zwei Unterstrichen schreiben:** + - ❌ `def _init_(self):` (wird nicht aufgerufen!) + - ✅ `def __init__(self):` (korrekt!) +2. **`self` bei jeder Methode als ersten Parameter:** + - ❌ `def einzahlen(betrag):` -> führt zu `TypeError: einzahlen() takes 1 positional argument but 2 were given` + - ✅ `def einzahlen(self, betrag):` +3. **Zuweisungsrichtung beachten:** + - ❌ `inhaber = self.inhaber` (falsch herum!) + - ✅ `self.inhaber = inhaber` (speichert den Parameter im Attribut) + +--- + ## 🎯 Deine Aufgabe: Das Bankkonto Öffne `aufgabe.py`. Du programmierst eine Klasse `Bankkonto`: diff --git a/02_init_und_self/aufgabe.py b/02_init_und_self/aufgabe.py index 3ad4a4b..3082ba7 100644 --- a/02_init_und_self/aufgabe.py +++ b/02_init_und_self/aufgabe.py @@ -10,9 +10,11 @@ class Bankkonto: # TODO 1: Schreibe die __init__-Methode. # Parameter: self, inhaber (String), kontostand (Float, Standardwert 0.0) # - # Speichere die Parameter als Attribute an 'self': - # - self.inhaber - # - self.kontostand + # Anleitung: + # 1. Definiere: def __init__(self, inhaber, kontostand=0.0): + # 2. Speichere die übergebenen Werte an self: + # self.inhaber = inhaber + # self.kontostand = kontostand # ========================================================================== def __init__(self, inhaber, kontostand=0.0): # Schreibe hier deinen Code für TODO 1: @@ -20,13 +22,14 @@ class Bankkonto: # ========================================================================== # TODO 2: Schreibe die Methode "einzahlen(self, betrag)". + # # Regeln: # 1. Wenn 'betrag' größer als 0 ist: - # - Erhöhe 'self.kontostand' um 'betrag'. + # - Erhöhe 'self.kontostand' um 'betrag': self.kontostand += betrag # - Gib True zurück (Einzahlung erfolgreich). - # 2. Wenn 'betrag' kleiner oder gleich 0 ist: - # - Verändere den Kontostand nicht. - # - Gib False zurück (ungültiger Betrag). + # 2. Wenn 'betrag' kleiner oder gleich 0 ist (z.B. 0 oder -5): + # - Ändere nichts am Kontostand. + # - Gib False zurück. # ========================================================================== def einzahlen(self, betrag): # Schreibe hier deinen Code für TODO 2: @@ -34,13 +37,12 @@ class Bankkonto: # ========================================================================== # TODO 3: Schreibe die Methode "auszahlen(self, betrag)". + # # Regeln: - # 1. Der Betrag muss größer als 0 sein UND es muss genug Geld auf dem Konto - # sein (self.kontostand >= betrag). - # - Ziehe 'betrag' von 'self.kontostand' ab. - # - Gib True zurück (Auszahlung erfolgreich). - # 2. Andernfalls (nicht genug Geld oder ungültiger Betrag): - # - Verändere den Kontostand nicht. + # 1. Prüfe mit 'if', ob 'betrag > 0' UND 'self.kontostand >= betrag': + # - Wenn ja: Ziehe den Betrag ab (self.kontostand -= betrag) + # - Gib True zurück. + # 2. Wenn nicht (nicht genug Geld da oder ungültiger Betrag): # - Gib False zurück. # ========================================================================== def auszahlen(self, betrag): @@ -49,10 +51,12 @@ class Bankkonto: # ========================================================================== # TODO 4: Schreibe die Methode "info_text(self)". - # Sie soll einen String im folgenden Format zurückgeben: + # + # Sie soll einen formatierten String zurückgeben: # "Konto von {inhaber}: {kontostand:.2f} Euro" # # Beispiel: Bei inhaber="Mia" und kontostand=25.5 -> "Konto von Mia: 25.50 Euro" + # Tipp: f"Konto von {self.inhaber}: {self.kontostand:.2f} Euro" # ========================================================================== def info_text(self): # Schreibe hier deinen Code für TODO 4: @@ -61,17 +65,22 @@ class Bankkonto: # ============================================================================== # Hauptprogramm zum Ausprobieren: +# (Direkt ausführen mit 'python3 aufgabe.py') # ============================================================================== if __name__ == "__main__": print("--- Teste dein Bankkonto ---") konto = Bankkonto("Mia", 20.0) - print(konto.info_text()) + + if hasattr(konto, "inhaber") and konto.inhaber: + print(konto.info_text()) - erfolg = konto.einzahlen(15.5) - print(f"15.50€ eingezahlt? {erfolg} -> Neuer Stand: {konto.kontostand}€") + erfolg = konto.einzahlen(15.5) + print(f"15.50€ eingezahlt? {erfolg} -> Neuer Stand: {konto.kontostand:.2f}€") - erfolg = konto.auszahlen(50.0) - print(f"50.00€ ausgezahlt? {erfolg} (sollte False sein) -> Stand: {konto.kontostand}€") + erfolg = konto.auszahlen(50.0) + print(f"50.00€ ausgezahlt? {erfolg} (sollte False sein) -> Stand: {konto.kontostand:.2f}€") - erfolg = konto.auszahlen(10.0) - print(f"10.00€ ausgezahlt? {erfolg} -> Stand: {konto.kontostand}€") + erfolg = konto.auszahlen(10.0) + print(f"10.00€ ausgezahlt? {erfolg} -> Stand: {konto.kontostand:.2f}€") + else: + print("💡 Hinweis: Implementiere erst die TODOs, um die Ausgabe zu sehen!") diff --git a/02_init_und_self/index.html b/02_init_und_self/index.html new file mode 100644 index 0000000..d9cc948 --- /dev/null +++ b/02_init_und_self/index.html @@ -0,0 +1,171 @@ + + + + + + Kapitel 02: Konstruktor __init__ und self + + + + +
+
+ +
Kapitel 02
+

Der Konstruktor __init__ & das geheimnisvolle self 💳

+

Lerne, wie Objekte direkt beim Erstellen startklar gemacht werden und baue eine Taschengeld-Konto App.

+
+
+ +
+ + +
+

1. Warum brauchen wir __init__?

+ +

+ In Kapitel 01 mussten wir jedes Attribut mühsam von Hand setzen: +

+ +
+
bello = Haustier()
+bello.name = "Bello"
+bello.alter = 3  # Was passiert, wenn man hier das Alter vergisst? -> Programm stürzt ab!
+
+ +

+ Deshalb gibt es den Konstruktor __init__. Er wird von Python automatisch aufgerufen, sobald ein neues Objekt erstellt wird! +

+ +
+
🏗️ Die Geburtsurkunde-Analogie:
+ Wenn ein neues Objekt "geboren" wird, muss es sofort seine grundlegenden Daten erhalten (Name, Startwert). __init__ ist wie das Ausfüllen des Datenblattes bei der Geburt – danach ist das Objekt sofort einsatzbereit! +
+
+ + +
+

2. Was bedeutet self?

+ +

+ Das Wörtchen self steht auf Deutsch für "ich selbst" bzw. "dieses konkrete Objekt hier". +

+ +
+
+
+ + + +
+ Python +
+
class Bankkonto:
+    def __init__(self, inhaber, kontostand=0.0):
+        self.inhaber = inhaber          # Speichere 'inhaber' im Attribut DIESES Objekts
+        self.kontostand = kontostand    # Speichere 'kontostand' in MEINEM Speicher
+
+# Verwendung:
+konto_anna = Bankkonto("Anna", 50.0)
+konto_ben = Bankkonto("Ben")          # Verwendet den Standardwert 0.0
+
+ +
+
💡 Warum self.inhaber = inhaber?
+ Links vom Gleichheitszeichen steht self.inhaber – das ist die Variable, die am Objekt dauerhaft gespeichert bleibt. Rechts steht inhaber – das ist nur der temporäre Übergabeparameter der Funktion. +
+
+ + +
+

3. Häufige Stolperfallen beim Konstruktor

+ +
+
⚠️ Fehler 1: Doppel-Unterstrich vs. Einzel-Unterstrich
+ Es heißt __init__ mit zwei Unterstrichen davor und danach (Dunder), NICHT _init_! Wenn du nur einen Unterstrich schreibst, ignoriert Python die Methode einfach. +
+ +
+
⚠️ Fehler 2: self im Methodenkopf vergessen
+ Jede Methode in einer Klasse muss als allerersten Parameter self haben: +
def einzahlen(self, betrag):  ✓ +
def einzahlen(betrag):  ✗ (Führt zu TypeError!) +
+
+ + +
+

4. Deine Aufgaben in aufgabe.py

+ +

Öffne die Datei 02_init_und_self/aufgabe.py:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ Tipp zu TODO 3 (Auszahlen mit Absicherung) +
+
+
def auszahlen(self, betrag):
+    if betrag > 0 and self.kontostand >= betrag:
+        self.kontostand -= betrag
+        return True
+    return False
+
+
+
+
+ + +
+

5. Lösung überprüfen

+

Führe im Terminal im Ordner 02_init_und_self folgenden Befehl aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/03_methoden_und_verhalten/README.md b/03_methoden_und_verhalten/README.md index 2791da9..2883b3f 100644 --- a/03_methoden_und_verhalten/README.md +++ b/03_methoden_und_verhalten/README.md @@ -56,6 +56,16 @@ class Spieler: --- +## ⚠️ Typische Stolperfallen in diesem Kapitel + +1. **Grenzen beachten (Min/Max Clamping):** + - Lebenspunkte dürfen nach Schaden nie negativ werden: `if self.leben < 0: self.leben = 0` + - Lebenspunkte dürfen nach Heilung nie über `max_leben` steigen: `self.leben = min(self.max_leben, self.leben + heilung)` +2. **Besiegte Objekte können nicht mehr handeln:** + - Vor Aktionen wie Angreifen oder Heilen immer prüfen: `if not self.ist_am_leben: return False` bzw. `return 0` + +--- + ## 🎯 Deine Aufgabe: Das RPG-Duell In `aufgabe.py` erstellst du eine Klasse `Held` für ein Rollenspiel: diff --git a/03_methoden_und_verhalten/aufgabe.py b/03_methoden_und_verhalten/aufgabe.py index bed2842..2060b3f 100644 --- a/03_methoden_und_verhalten/aufgabe.py +++ b/03_methoden_und_verhalten/aufgabe.py @@ -8,7 +8,8 @@ In dieser Aufgabe programmierst du eine Klasse 'Held' für ein Rollenspiel. class Held: # ========================================================================== # TODO 1: Konstruktor __init__(self, name, leben=100, angriffskraft=15) - # Speichere: + # + # Speichere die folgenden Attribute an self: # - self.name = name # - self.leben = leben # - self.max_leben = leben (das maximale Leben entspricht dem Startleben) @@ -21,14 +22,15 @@ class Held: # ========================================================================== # TODO 2: Methode "schaden_erleiden(self, schaden)" - # Regeln: - # 1. Wenn der Held bereits besiegt ist (not self.ist_am_leben) oder schaden <= 0: - # - Es passiert nichts, gib 0 zurück. - # 2. Ziehe 'schaden' von 'self.leben' ab. - # 3. Wenn 'self.leben' <= 0 wird: - # - Setze 'self.leben' genau auf 0. - # - Setze 'self.ist_am_leben' auf False. - # 4. Gib den erlittenen Schaden (int/float) zurück. + # + # Schritt-für-Schritt: + # 1. Wenn der Held schon tot ist (not self.ist_am_leben) oder schaden <= 0: + # - return 0 + # 2. Berechne den Schaden und ziehe ihn von self.leben ab. + # 3. Wenn self.leben <= 0 wird: + # - setze self.leben = 0 + # - setze self.ist_am_leben = False + # 4. Gib den erlittenen Schaden zurück (z.B. return schaden). # ========================================================================== def schaden_erleiden(self, schaden): # Schreibe hier deinen Code für TODO 2: @@ -36,14 +38,16 @@ class Held: # ========================================================================== # TODO 3: Methode "heilen(self, heilung)" - # Regeln: - # 1. Wenn der Held besiegt ist (not self.ist_am_leben) oder heilung <= 0: - # - Tote Helden können nicht geheilt werden! Gib 0 zurück. - # 2. Erhöhe das Leben um 'heilung', aber ACHTUNG: - # - 'self.leben' darf niemals größer als 'self.max_leben' werden! - # - Beispiel: Wenn leben=90, max_leben=100 und heilung=20: - # Das Leben wird 100 (nicht 110) und die tatsächliche Heilung war 10. - # 3. Gib die tatsächlich geheilte Menge an Lebenspunkten zurück. + # + # Schritt-für-Schritt: + # 1. Wenn der Held tot ist (not self.ist_am_leben) oder heilung <= 0: + # - return 0 (Tote können nicht geheilt werden!) + # 2. Merke dir das aktuelle Leben: alter_wert = self.leben + # 3. Erhöhe das Leben, aber maximal bis self.max_leben: + # self.leben = min(self.max_leben, self.leben + heilung) + # 4. Berechne, wie viel tatsächlich geheilt wurde: + # tatsaechlich_geheilt = self.leben - alter_wert + # 5. Gib tatsaechlich_geheilt zurück. # ========================================================================== def heilen(self, heilung): # Schreibe hier deinen Code für TODO 3: @@ -52,13 +56,15 @@ class Held: # ========================================================================== # TODO 4: Methode "angreifen(self, gegner)" # Parameter 'gegner' ist ein anderes Held-Objekt! - # Regeln: - # 1. Prüfe, ob BEIDE Helden noch am Leben sind (self.ist_am_leben und gegner.ist_am_leben). + # + # Schritt-für-Schritt: + # 1. Prüfe, ob BEIDE Helden leben: self.ist_am_leben and gegner.ist_am_leben # 2. Wenn ja: - # - Rufe auf dem 'gegner' die Methode 'schaden_erleiden' auf mit self.angriffskraft als Wert. - # - Gib True zurück. - # 3. Wenn einer von beiden (oder beide) nicht mehr am Leben ist: - # - Greife nicht an und gib False zurück. + # - Rufe auf dem Gegner schaden_erleiden auf: + # gegner.schaden_erleiden(self.angriffskraft) + # - Gib True zurück (Angriff erfolgreich). + # 3. Wenn einer von beiden besiegt ist: + # - Gib False zurück. # ========================================================================== def angreifen(self, gegner): # Schreibe hier deinen Code für TODO 4: @@ -66,6 +72,7 @@ class Held: # ========================================================================== # TODO 5: Methode "status_text(self)" + # # Gibt einen formatierten String zurück: # Wenn am Leben: # "[{self.name}] HP: {self.leben}/{self.max_leben} | Kraft: {self.angriffskraft} | Status: Lebendig" @@ -79,23 +86,27 @@ class Held: # ============================================================================== # Kampfsimulator zum Ausprobieren: +# (Ausführen mit 'python3 aufgabe.py') # ============================================================================== if __name__ == "__main__": print("⚔️ --- ARENA DER HELDEN --- ⚔️") ritter = Held("Arthur", leben=80, angriffskraft=20) drache = Held("Feuerspei", leben=120, angriffskraft=25) - print(ritter.status_text()) - print(drache.status_text()) + if hasattr(ritter, "name") and ritter.name: + print(ritter.status_text()) + print(drache.status_text()) - print("\n--- Runde 1 ---") - ritter.angreifen(drache) - print(drache.status_text()) + print("\n--- Runde 1 ---") + ritter.angreifen(drache) + print(drache.status_text()) - drache.angreifen(ritter) - print(ritter.status_text()) + drache.angreifen(ritter) + print(ritter.status_text()) - print("\n--- Arthur trinkt einen Heiltrank ---") - geheilt = ritter.heilen(15) - print(f"Arthur heilt sich um {geheilt} HP!") - print(ritter.status_text()) + print("\n--- Arthur trinkt einen Heiltrank ---") + geheilt = ritter.heilen(15) + print(f"Arthur heilt sich um {geheilt} HP!") + print(ritter.status_text()) + else: + print("💡 Hinweis: Implementiere erst die TODOs, um die Arena zu starten!") diff --git a/03_methoden_und_verhalten/index.html b/03_methoden_und_verhalten/index.html new file mode 100644 index 0000000..e501f41 --- /dev/null +++ b/03_methoden_und_verhalten/index.html @@ -0,0 +1,152 @@ + + + + + + Kapitel 03: Methoden und Verhalten + + + + +
+
+ +
Kapitel 03
+

Methoden & Verhalten: Wenn Objekte lebendig werden ⚔️

+

Lass Objekte aktiv handeln und miteinander interagieren in einem rundenbasierten RPG-Heldenduell.

+
+
+ +
+ + +
+

1. Was sind Methoden?

+ +

+ Bisher haben unsere Klassen vor allem Daten gespeichert. Aber in der objektorientierten Programmierung haben Objekte auch Verhalten. Funktionen, die zu einer Klasse gehören, nennt man Methoden. +

+ +
+
+
+ + + +
+ Python +
+
class Zauberer:
+    def __init__(self, name, mana=50):
+        self.name = name
+        self.mana = mana
+
+    # Eine Methode gehört dem Zauberer:
+    def zauberspruch_wirken(self, kosten):
+        if self.mana >= kosten:
+            self.mana -= kosten
+            return True
+        return False
+
+
+ + +
+

2. Wenn zwei Objekte miteinander interagieren

+ +

+ Besonders spannend wird es, wenn eine Methode eines Objekts ein anderes Objekt als Parameter bekommt (z.B. ritter.angreifen(drache)). +

+ +
+
🤝 Die Duell-Analogie:
+ In angreifen(self, gegner) ist self der Angreifer (z.B. Arthur) und gegner ist das Ziel (z.B. der Drache). Arthur ruft gegner.schaden_erleiden(self.angriffskraft) auf. So beeinflussen sich die Objekte gegenseitig! +
+ +
+
⚠️ Wichtig: Logische Grenzen einhalten (Clamping)!
+ Lebenspunkte dürfen niemals unter 0 fallen (ein besiegter Held hat 0 HP, nicht -15 HP) und bei Heilung niemals über max_leben steigen. +
+
+ + +
+

3. Deine Aufgaben in aufgabe.py

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ Tipp zu TODO 3 (Heilung begrenzen) +
+
+
def heilen(self, heilung):
+    if not self.ist_am_leben or heilung <= 0:
+        return 0
+    alter_wert = self.leben
+    self.leben = min(self.max_leben, self.leben + heilung)
+    return self.leben - alter_wert  # Wie viel wurde wirklich geheilt?
+
+
+
+
+ + +
+

4. Lösung überprüfen

+

Führe im Terminal im Ordner 03_methoden_und_verhalten folgenden Befehl aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/04_str_und_darstellung/README.md b/04_str_und_darstellung/README.md index d121e0d..1e4026d 100644 --- a/04_str_und_darstellung/README.md +++ b/04_str_und_darstellung/README.md @@ -60,6 +60,16 @@ class Schultasche: --- +## ⚠️ Stolperfallen bei `__str__` + +1. **`__str__` muss immer einen `str` zurückgeben (`return`), NIEMALS `print`:** + - ❌ `def __str__(self): print(self.name)` (Falsch!) + - ✅ `def __str__(self): return str(self.name)` (Richtig!) +2. **Unterstriche beachten:** + - Zwei Unterstriche vor und nach `str`: `__str__`. + +--- + ## 🎯 Deine Aufgabe: Der Supermarkt-Kassenzettel In `aufgabe.py` programmierst du zwei Klassen: diff --git a/04_str_und_darstellung/aufgabe.py b/04_str_und_darstellung/aufgabe.py index 445cb0d..24e767f 100644 --- a/04_str_und_darstellung/aufgabe.py +++ b/04_str_und_darstellung/aufgabe.py @@ -19,6 +19,7 @@ class Artikel: # TODO 2: Methode "gesamtpreis(self)" # Gibt den Preis für diesen Artikel multipliziert mit der Anzahl zurück. # Beispiel: 3 Äpfel à 0.50€ -> 1.50 + # Tipp: return self.preis * self.anzahl # ========================================================================== def gesamtpreis(self): # Schreibe hier deinen Code für TODO 2: @@ -26,11 +27,10 @@ class Artikel: # ========================================================================== # TODO 3: Dunder-Methode "__str__(self)" - # Gibt einen String im folgenden Format zurück: + # Gibt einen formatierten String zurück: # "{anzahl}x {name} (je {preis:.2f} €) = {gesamtpreis:.2f} €" # - # Beispiel: - # Bei name="Milch", preis=1.29, anzahl=2: + # Beispiel: Bei name="Milch", preis=1.29, anzahl=2: # -> "2x Milch (je 1.29 €) = 2.58 €" # ========================================================================== def __str__(self): @@ -50,7 +50,7 @@ class Warenkorb: # ========================================================================== # TODO 5: Methode "artikel_hinzufuegen(self, artikel)" - # Fügt das übergebene Artikel-Objekt an 'self.artikel_liste' an (.append). + # Hängt das übergebene Artikel-Objekt an 'self.artikel_liste' an (.append). # ========================================================================== def artikel_hinzufuegen(self, artikel): # Schreibe hier deinen Code für TODO 5: @@ -59,8 +59,13 @@ class Warenkorb: # ========================================================================== # TODO 6: Methode "gesamtsumme(self)" # Berechnet die Summe der Gesamtpreise aller Artikel in self.artikel_liste - # und gibt diese als float zurück. (Tipp: Schleife oder sum()) - # Bei leerem Warenkorb soll 0.0 zurückgegeben werden. + # und gibt diese als float zurück. + # + # Tipp mit Schleife: + # total = 0.0 + # for item in self.artikel_liste: + # total += item.gesamtpreis() + # return total # ========================================================================== def gesamtsumme(self): # Schreibe hier deinen Code für TODO 6: @@ -76,8 +81,7 @@ class Warenkorb: # -------------------- # Gesamtsumme: 5.07 € # - # Tipp: Baue eine Liste von Zeilen und verbinde sie mit "\n".join(...) - # Die Zeilen für die Artikel erhältst du einfach mit str(artikel)! + # Tipp: Baue eine Liste von Zeilen und verbinde sie am Ende mit "\n".join(...) # ========================================================================== def bon_text(self): # Schreibe hier deinen Code für TODO 7: @@ -86,6 +90,7 @@ class Warenkorb: # ============================================================================== # Hauptprogramm zum Ausprobieren: +# (Ausführen mit 'python3 aufgabe.py') # ============================================================================== if __name__ == "__main__": print("🛒 --- SUPERMARKT EINKAUF --- 🛒") @@ -95,11 +100,10 @@ if __name__ == "__main__": a2 = Artikel("Brot", 2.49, 1) a3 = Artikel("Schokolade", 0.99, 3) - korb.artikel_hinzufuegen(a1) - korb.artikel_hinzufuegen(a2) - korb.artikel_hinzufuegen(a3) - - if korb.artikel_liste: + if hasattr(korb, "artikel_liste"): + korb.artikel_hinzufuegen(a1) + korb.artikel_hinzufuegen(a2) + korb.artikel_hinzufuegen(a3) print(korb.bon_text()) else: - print("Hinweis: Implementiere die TODOs, um den Kassenbon zu drucken!") + print("💡 Hinweis: Implementiere die TODOs, um den Kassenbon zu drucken!") diff --git a/04_str_und_darstellung/index.html b/04_str_und_darstellung/index.html new file mode 100644 index 0000000..6fae5f0 --- /dev/null +++ b/04_str_und_darstellung/index.html @@ -0,0 +1,155 @@ + + + + + + Kapitel 04: __str__ und Darstellung + + + + +
+
+ +
Kapitel 04
+

Schöne Ausgaben mit __str__ & Objektlisten 🧾

+

Lerne, wie du bestimmst, was beim print(objekt) erscheint und wie Objekte andere Objekte in Listen verwalten.

+
+
+ +
+ + +
+

1. Warum sieht print(objekt) standardmäßig so hässlich aus?

+ +

+ Wenn du versuchst, ein Objekt ohne Dunder-Methode zu drucken: +

+ +
+
mein_buch = Buch("Harry Potter", "J.K. Rowling")
+print(mein_buch)  # Ausgabe: <__main__.Buch object at 0x7fa2b0485d90>
+
+ +

+ Python weiß ohne deine Hilfe nicht, wie du dein Objekt darstellen möchtest. +

+ +
+
✨ Die Magie von __str__:
+ Sobald du in deiner Klasse eine Methode def __str__(self): definierst, ruft Python diese automatisch auf, wann immer das Objekt zu Text umgewandelt wird (bei print(), str() oder in f-Strings)! +
+ +
+
+
+ + + +
+ Python +
+
class Buch:
+    def __init__(self, titel, autor):
+        self.titel = titel
+        self.autor = autor
+
+    def __str__(self):
+        return f"'{self.titel}' von {self.autor}"  # MUSS ein String sein!
+
+mein_buch = Buch("Harry Potter", "J.K. Rowling")
+print(mein_buch)  # Ausgabe: 'Harry Potter' von J.K. Rowling
+
+
+ + +
+

2. Objekte in Listen verwalten

+ +

+ Eine Klasse kann als Attribut eine Liste von anderen Objekten haben: +

+ +
+
🛒 Der Warenkorb:
+ Ein Warenkorb besitzt eine Liste self.artikel_liste = []. Wenn du einen Artikel hinzufügst, landet das ganze Artikel-Objekt in der Liste! +
+
+ + +
+

3. Deine Aufgaben in aufgabe.py

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ Tipp zu TODO 7 (Kassenzettel zusammensetzen) +
+
+
def bon_text(self):
+    zeilen = ["--- KASSENZETTEL ---"]
+    for artikel in self.artikel_liste:
+        zeilen.append(str(artikel))  # Nutzt automatisch __str__ des Artikels!
+    zeilen.append("--------------------")
+    zeilen.append(f"Gesamtsumme: {self.gesamtsumme():.2f} €")
+    return "\n".join(zeilen)
+
+
+
+
+ + +
+

4. Lösung überprüfen

+

Führe im Terminal im Ordner 04_str_und_darstellung folgenden Befehl aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/05_objekte_kombinieren/aufgabe.py b/05_objekte_kombinieren/aufgabe.py index c8436d9..6888c38 100644 --- a/05_objekte_kombinieren/aufgabe.py +++ b/05_objekte_kombinieren/aufgabe.py @@ -20,7 +20,11 @@ class Song: # TODO 2: Methode "formatierte_dauer(self)" # Rechnet 'self.dauer_sekunden' in Minuten und Sekunden um und gibt einen # String im Format "MM:SS" zurück (z.B. 215 -> "03:35", 70 -> "01:10"). - # Tipp: Verwende // 60 und % 60 sowie f"{minuten:02d}:{sekunden:02d}" + # + # Tipp: + # minuten = self.dauer_sekunden // 60 + # sekunden = self.dauer_sekunden % 60 + # return f"{minuten:02d}:{sekunden:02d}" # ========================================================================== def formatierte_dauer(self): # Schreibe hier deinen Code für TODO 2: @@ -28,8 +32,8 @@ class Song: # ========================================================================== # TODO 3: Dunder-Methode "__str__(self)" - # Gibt einen String im folgenden Format zurück: - # '"{titel}" von {kuenstler} ({dauer})' + # Gibt einen String im Format zurück: + # '"{titel}" von {kuenstler} ({formatierte_dauer})' # # Beispiel: '"Flowers" von Miley Cyrus (03:20)' # ========================================================================== @@ -50,7 +54,7 @@ class Playlist: # ========================================================================== # TODO 5: Methode "song_hinzufuegen(self, song)" - # Hängt das übergebene Song-Objekt an 'self.songs' an. + # Hängt das übergebene Song-Objekt an 'self.songs' an (.append). # ========================================================================== def song_hinzufuegen(self, song): # Schreibe hier deinen Code für TODO 5: @@ -58,7 +62,7 @@ class Playlist: # ========================================================================== # TODO 6: Methode "anzahl_songs(self)" - # Gibt die Anzahl der Songs in der Playlist als Integer zurück. + # Gibt die Anzahl der Songs in der Playlist als Integer zurück (len(self.songs)). # ========================================================================== def anzahl_songs(self): # Schreibe hier deinen Code für TODO 6: @@ -68,6 +72,12 @@ class Playlist: # TODO 7: Methode "gesamtdauer_sekunden(self)" # Gibt die Summe der Sekunden aller Songs in der Playlist zurück. # Bei leerer Playlist: 0 + # + # Tipp: + # total = 0 + # for s in self.songs: + # total += s.dauer_sekunden + # return total # ========================================================================== def gesamtdauer_sekunden(self): # Schreibe hier deinen Code für TODO 7: @@ -96,6 +106,7 @@ class Playlist: # ============================================================================== # Hauptprogramm zum Ausprobieren: +# (Ausführen mit 'python3 aufgabe.py') # ============================================================================== if __name__ == "__main__": print("🎧 --- MEINE PLAYLIST --- 🎧") @@ -106,18 +117,21 @@ if __name__ == "__main__": s3 = Song("Anti-Hero", "Taylor Swift", 201) s4 = Song("As It Was", "Harry Styles", 167) - meine_hits.song_hinzufuegen(s1) - meine_hits.song_hinzufuegen(s2) - meine_hits.song_hinzufuegen(s3) - meine_hits.song_hinzufuegen(s4) + if hasattr(meine_hits, "songs"): + meine_hits.song_hinzufuegen(s1) + meine_hits.song_hinzufuegen(s2) + meine_hits.song_hinzufuegen(s3) + meine_hits.song_hinzufuegen(s4) - print(f"Playlist: {meine_hits.name} ({meine_hits.anzahl_songs()} Songs)") - print(f"Gesamtlaufzeit: {meine_hits.gesamtdauer_sekunden()} Sekunden") + print(f"Playlist: {meine_hits.name} ({meine_hits.anzahl_songs()} Songs)") + print(f"Gesamtlaufzeit: {meine_hits.gesamtdauer_sekunden()} Sekunden") - laengster = meine_hits.laengster_song() - if laengster: - print(f"Längster Song: {laengster}") + laengster = meine_hits.laengster_song() + if laengster: + print(f"Längster Song: {laengster}") - print("\nSongs von Taylor Swift:") - for s in meine_hits.finde_songs_von("taylor swift"): - print(f"- {s}") + print("\nSongs von Taylor Swift:") + for s in meine_hits.finde_songs_von("taylor swift"): + print(f"- {s}") + else: + print("💡 Hinweis: Implementiere die TODOs, um die Playlist zu testen!") diff --git a/05_objekte_kombinieren/index.html b/05_objekte_kombinieren/index.html new file mode 100644 index 0000000..9f11649 --- /dev/null +++ b/05_objekte_kombinieren/index.html @@ -0,0 +1,150 @@ + + + + + + Kapitel 05: Komposition und Playlists + + + + +
+
+ +
Kapitel 05
+

Komposition: Objekte kombinieren & filtern 🎵

+

Baue einen eigenen Spotify-Playlist-Manager und lerne, wie Objekte andere Objekte suchen, berechnen und filtern.

+
+
+ +
+ + +
+

1. Was ist Komposition?

+ +

+ In großen Anwendungen (wie Spotify, Netflix oder Videospielen) arbeiten viele Klassen zusammen. Wenn eine Klasse Instanzen einer anderen Klasse beinhaltet, nennt man das Komposition (oder *"hat-ein"-Beziehung*): +

+ +
    +
  • Eine Playlist hat viele Songs.
  • +
  • Ein Kino hat viele Säle und Filme.
  • +
+ +
+
🎧 Die Spotify-Analogie:
+ Ein Song speichert seinen Titel, den Künstler und die Länge in Sekunden. Die Playlist kümmert sich um die Gesamtübersicht: Wie viele Songs gibt es? Wie lange dauert die Playlist insgesamt? Gibt es Lieder von Taylor Swift? +
+
+ + +
+

2. Nützliche Python-Kniffe

+ +

⏱️ Sekunden in "MM:SS" umrechnen

+

+ Mit Ganzzahldivision // und Modulo % rechnen wir Sekunden in Minuten um: +

+ +
+
+ Python +
+
gesamt_sekunden = 215
+minuten = gesamt_sekunden // 60   # 3 Minuten
+sekunden = gesamt_sekunden % 60   # 35 Restsekunden
+
+# :02d sorgt für zweistellige Zahlen mit führender Null (z.B. 03:05 statt 3:5):
+zeit_text = f"{minuten:02d}:{sekunden:02d}"  # "03:35"
+
+ +

🔍 Groß- und Kleinschreibung beim Suchen ignorieren

+

+ Damit "Taylor Swift" auch gefunden wird, wenn jemand "taylor swift" eingibt, nutzen wir .lower(): +

+ +
+
if song.kuenstler.lower() == gesuchter_kuenstler.lower():
+    treffer.append(song)
+
+
+ + +
+

3. Deine Aufgaben in aufgabe.py

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ Tipp zum längsten Song finden +
+
+
def laengster_song(self):
+    if not self.songs:
+        return None
+    
+    bester_song = self.songs[0]
+    for s in self.songs:
+        if s.dauer_sekunden > bester_song.dauer_sekunden:
+            bester_song = s
+    return bester_song
+
+
+
+
+ + +
+

4. Lösung überprüfen

+

Führe im Terminal im Ordner 05_objekte_kombinieren folgenden Befehl aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/06_abschlussprojekt_tamagotchi/README.md b/06_abschlussprojekt_tamagotchi/README.md index 91a5b58..e8bd336 100644 --- a/06_abschlussprojekt_tamagotchi/README.md +++ b/06_abschlussprojekt_tamagotchi/README.md @@ -23,7 +23,7 @@ Du kannst mit ihm interagieren: - 🍎 **Füttern** (`fuettern`): Senkt den Hunger, macht etwas müde und etwas glücklicher. - ⚽ **Spielen** (`spielen`): Steigert das Glück, macht aber hungrig und müde. Zu müde Haustiere weigern sich zu spielen! - 💤 **Schlafen** (`schlafen`): Setzt die Müdigkeit auf 0, lässt einen Tag vergehen (`alter_tage += 1`), macht aber etwas hungrig. -- ⏳ **Zeit vergeht** (`zeit_vergeht`): Das Haustier wird hungriger, müder und etwas trauriger. Wenn der Hunger 100 erreicht, schläft es für immer ein... +- ⏳ **Zeit vergeht** (`zeit_vergeht`): Das Haustier wird hungriger, müder und etwas trauriger. Wenn der Hunger 100 erreicht oder Glück 0 wird, schläft es für immer ein... --- diff --git a/06_abschlussprojekt_tamagotchi/aufgabe.py b/06_abschlussprojekt_tamagotchi/aufgabe.py index 00d238a..9e20e16 100644 --- a/06_abschlussprojekt_tamagotchi/aufgabe.py +++ b/06_abschlussprojekt_tamagotchi/aufgabe.py @@ -9,7 +9,7 @@ class Tamagotchi: # ========================================================================== # TODO 1: Konstruktor __init__(self, name, tierart="Drache") # - # Initialisiere die folgenden Attribute: + # Initialisiere die folgenden Attribute an self: # - self.name = name # - self.tierart = tierart # - self.hunger = 50 (0 = pappsatt, 100 = verhungert) @@ -22,10 +22,9 @@ class Tamagotchi: # Schreibe hier deinen Code für TODO 1: pass - # Hilfsmethode (optional, aber sehr nützlich): - # Hält einen Wert zwischen minimal 0 und maximal 100. - def _begrenze(self, wert): - return max(0, min(100, wert)) + # Hilfsmethode: Begrenzt Werte immer zwischen min_wert (0) und max_wert (100) + def _begrenze(self, wert, min_wert=0, max_wert=100): + return max(min_wert, min(max_wert, wert)) # ========================================================================== # TODO 2: Methode "fuettern(self, menge=20)" @@ -34,9 +33,9 @@ class Tamagotchi: # 1. Wenn not self.ist_lebendig: # - Gib zurück: f"{self.name} reagiert nicht mehr..." # 2. Wenn lebendig: - # - Verringere 'self.hunger' um 'menge' (darf nicht unter 0 fallen). - # - Erhöhe 'self.glueck' um 5 (darf nicht über 100 steigen). - # - Erhöhe 'self.muedigkeit' um 5 (darf nicht über 100 steigen). + # - Verringere self.hunger um menge (mit _begrenze, darf nicht unter 0 fallen). + # - Erhöhe self.glueck um 5 (max 100). + # - Erhöhe self.muedigkeit um 5 (max 100). # - Gib zurück: f"{self.name} mampft genüsslich! (Hunger: {self.hunger}/100)" # ========================================================================== def fuettern(self, menge=20): @@ -49,13 +48,13 @@ class Tamagotchi: # Regeln: # 1. Wenn not self.ist_lebendig: # - Gib zurück: f"{self.name} reagiert nicht mehr..." - # 2. Wenn 'self.muedigkeit' > 80 ist: + # 2. Wenn self.muedigkeit > 80 ist: # - Zu müde zum Spielen! Ändere keine Werte und gib zurück: # f"{self.name} ist zu müde zum Spielen und gähnt nur..." # 3. Wenn fit genug: - # - Erhöhe 'self.glueck' um 'spass' (max 100). - # - Erhöhe 'self.hunger' um 15 (max 100). - # - Erhöhe 'self.muedigkeit' um 20 (max 100). + # - Erhöhe self.glueck um spass (max 100). + # - Erhöhe self.hunger um 15 (max 100). + # - Erhöhe self.muedigkeit um 20 (max 100). # - Gib zurück: f"{self.name} hatte riesigen Spaß beim Spielen! (Glück: {self.glueck}/100)" # ========================================================================== def spielen(self, spass=25): @@ -69,9 +68,9 @@ class Tamagotchi: # 1. Wenn not self.ist_lebendig: # - Gib zurück: f"{self.name} reagiert nicht mehr..." # 2. Wenn lebendig: - # - Setze 'self.muedigkeit' auf 0. - # - Erhöhe 'self.hunger' um 15 (max 100). - # - Erhöhe 'self.alter_tage' um 1. + # - Setze self.muedigkeit auf 0. + # - Erhöhe self.hunger um 15 (max 100). + # - Erhöhe self.alter_tage um 1. # - Gib zurück: f"{self.name} hat tief geschlafen und ist ausgeruht! Ein neuer Tag bricht an." # ========================================================================== def schlafen(self): @@ -86,9 +85,9 @@ class Tamagotchi: # 1. Wenn not self.ist_lebendig: # - Mach nichts, gib False zurück. # 2. Wenn lebendig: - # - Erhöhe 'self.hunger' um 10 (max 100). - # - Erhöhe 'self.muedigkeit' um 10 (max 100). - # - Verringere 'self.glueck' um 10 (min 0). + # - Erhöhe self.hunger um 10 (max 100). + # - Erhöhe self.muedigkeit um 10 (max 100). + # - Verringere self.glueck um 10 (min 0). # - Prüfe, ob das Haustier stirbt: # Wenn self.hunger >= 100 oder self.glueck <= 0: # self.ist_lebendig = False @@ -118,12 +117,16 @@ class Tamagotchi: # ============================================================================== # Schneller Testlauf: +# (Ausführen mit 'python3 aufgabe.py' oder starte 'python3 tamagotchi_spiel.py') # ============================================================================== if __name__ == "__main__": pet = Tamagotchi("Yoshi", "Dino") - print(pet) - print() - print(pet.fuettern()) - print(pet.spielen()) - print(pet.schlafen()) - print(pet) + if hasattr(pet, "name") and pet.name: + print(pet) + print() + print(pet.fuettern()) + print(pet.spielen()) + print(pet.schlafen()) + print(pet) + else: + print("💡 Hinweis: Implementiere erst die TODOs, um Yoshi zum Leben zu erwecken!") diff --git a/06_abschlussprojekt_tamagotchi/index.html b/06_abschlussprojekt_tamagotchi/index.html new file mode 100644 index 0000000..f2de1d3 --- /dev/null +++ b/06_abschlussprojekt_tamagotchi/index.html @@ -0,0 +1,133 @@ + + + + + + Kapitel 06: Tamagotchi Abschlussprojekt + + + + +
+
+ +
Abschlussprojekt
+

Dein eigenes Tamagotchi (Virtuelles Haustier) 🥚🐣

+

Kombiniere alle Konzepte in einem interaktiven, voll funktionsfähigen Konsolenspiel!

+
+
+ +
+ + +
+

1. Das große Finale! 🏆

+ +

+ Du hast alle Grundlagen gelernt: Klassen definieren, __init__ & self nutzen, Methoden schreiben, __str__ gestalten und Objekte verwalten. +

+

+ Jetzt bauen wir ein komplettes, lebendiges Tamagotchi, das du im Terminal füttern, schlafen legen und mit dem du spielen kannst! +

+ +
+
🎮 Wie das Spiel funktioniert:
+
    +
  • 🍖 Hunger (0 bis 100): Steigt mit der Zeit, sinkt beim Füttern. Bei 100 stirbt das Haustier!
  • +
  • 💤 Müdigkeit (0 bis 100): Steigt bei Aktionen. Bei über 80 weigert es sich zu spielen. Schlafen setzt Müdigkeit auf 0.
  • +
  • 💖 Glück (0 bis 100): Steigt beim Spielen, sinkt mit der Zeit. Bei 0 stirbt das Haustier vor Einsamkeit.
  • +
  • 📅 Alter: Bei jedem Schlafen vergeht ein neuer Tag (alter_tage += 1).
  • +
+
+
+ + +
+

2. Deine Aufgaben in aufgabe.py

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ Tipp zur Werte-Begrenzung (Clamping) +
+

Damit Werte nicht negativ werden oder 100 überschreiten, kannst du die Hilfsfunktion max(0, min(100, wert)) nutzen:

+
+
self.hunger = max(0, min(100, self.hunger - menge))
+
+
+
+
+ + +
+

3. Das interaktive Spiel starten! 🕹️

+ +

Sobald deine Tests grün sind, kannst du das Spiel live spielen:

+ +
+
+ Terminal +
+
python3 tamagotchi_spiel.py
+
+ +
+
🎉 Geschafft!
+ Du hast damit erfolgreich gelernt, wie man objektorientiert in Python programmiert! +
+ + +
+ +
+ + + diff --git a/assets/style.css b/assets/style.css new file mode 100644 index 0000000..fdf6bdc --- /dev/null +++ b/assets/style.css @@ -0,0 +1,449 @@ +/* ========================================================================== + Modernes Design-System für Python OOP Lernportal + ========================================================================== */ + +:root { + --primary: #6366f1; + --primary-hover: #4f46e5; + --primary-light: #eef2ff; + --secondary: #06b6d4; + --success: #10b981; + --success-light: #ecfdf5; + --warning: #f59e0b; + --warning-light: #fffbeb; + --danger: #ef4444; + --danger-light: #fef2f2; + --purple: #8b5cf6; + --purple-light: #f5f3ff; + + --bg-main: #f8fafc; + --bg-card: #ffffff; + --bg-code: #1e293b; + --border-color: #e2e8f0; + --text-main: #1e293b; + --text-muted: #64748b; + --text-code: #f1f5f9; + + --radius-sm: 6px; + --radius-md: 12px; + --radius-lg: 18px; + --shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04); + --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.08), 0 2px 4px -1px rgba(0,0,0,0.04); + --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.08), 0 4px 6px -2px rgba(0,0,0,0.04); + --font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + --font-mono: 'Fira Code', 'JetBrains Mono', Consolas, Monaco, 'Courier New', monospace; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: var(--font-sans); + background-color: var(--bg-main); + color: var(--text-main); + line-height: 1.65; + font-size: 16px; + padding-bottom: 60px; +} + +/* Header & Navigation */ +header { + background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); + color: white; + padding: 30px 20px; + box-shadow: var(--shadow-md); + margin-bottom: 35px; +} + +.header-container { + max-width: 900px; + margin: 0 auto; +} + +.nav-links { + display: flex; + gap: 15px; + margin-bottom: 15px; + font-size: 0.9rem; + flex-wrap: wrap; +} + +.nav-links a { + color: rgba(255, 255, 255, 0.85); + text-decoration: none; + background: rgba(255, 255, 255, 0.15); + padding: 4px 12px; + border-radius: 20px; + transition: all 0.2s; + display: inline-flex; + align-items: center; + gap: 6px; +} + +.nav-links a:hover { + background: rgba(255, 255, 255, 0.3); + color: white; + transform: translateY(-1px); +} + +.header-badge { + display: inline-block; + background: rgba(255, 255, 255, 0.2); + padding: 4px 12px; + border-radius: 20px; + font-size: 0.85rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 8px; +} + +header h1 { + font-size: 2.1rem; + font-weight: 800; + margin-bottom: 6px; + line-height: 1.25; +} + +header p { + font-size: 1.1rem; + opacity: 0.9; +} + +/* Main Container */ +.container { + max-width: 900px; + margin: 0 auto; + padding: 0 20px; +} + +/* Content Cards */ +.card { + background: var(--bg-card); + border-radius: var(--radius-md); + padding: 28px; + margin-bottom: 25px; + border: 1px solid var(--border-color); + box-shadow: var(--shadow-sm); +} + +.card h2 { + font-size: 1.45rem; + color: var(--primary); + margin-bottom: 16px; + display: flex; + align-items: center; + gap: 10px; + border-bottom: 2px solid var(--primary-light); + padding-bottom: 8px; +} + +.card h3 { + font-size: 1.2rem; + margin: 20px 0 10px 0; + color: var(--text-main); +} + +.card p { + margin-bottom: 14px; +} + +/* Didactic Callout Boxes */ +.box { + border-radius: var(--radius-md); + padding: 18px 22px; + margin: 20px 0; + border-left: 5px solid; +} + +.box-title { + font-weight: 700; + font-size: 1.05rem; + margin-bottom: 8px; + display: flex; + align-items: center; + gap: 8px; +} + +.box-analogie { + background-color: var(--purple-light); + border-left-color: var(--purple); + color: #4c1d95; +} + +.box-tipp { + background-color: var(--primary-light); + border-left-color: var(--primary); + color: #312e81; +} + +.box-warning { + background-color: var(--warning-light); + border-left-color: var(--warning); + color: #78350f; +} + +.box-danger { + background-color: var(--danger-light); + border-left-color: var(--danger); + color: #7f1d1d; +} + +.box-success { + background-color: var(--success-light); + border-left-color: var(--success); + color: #064e3b; +} + +/* Code Blocks */ +.code-container { + background-color: var(--bg-code); + border-radius: var(--radius-md); + margin: 18px 0; + overflow: hidden; + box-shadow: var(--shadow-sm); +} + +.code-header { + background-color: #0f172a; + color: #94a3b8; + padding: 8px 16px; + font-size: 0.8rem; + font-family: var(--font-mono); + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); +} + +.code-dots { + display: flex; + gap: 6px; +} + +.code-dot { + width: 10px; + height: 10px; + border-radius: 50%; +} +.dot-red { background-color: #ef4444; } +.dot-yellow { background-color: #f59e0b; } +.dot-green { background-color: #10b981; } + +pre { + margin: 0; + padding: 16px; + overflow-x: auto; + font-family: var(--font-mono); + font-size: 0.92rem; + color: var(--text-code); + line-height: 1.55; +} + +code { + font-family: var(--font-mono); + font-size: 0.9em; + background-color: #f1f5f9; + color: #e11d48; + padding: 2px 6px; + border-radius: var(--radius-sm); +} + +pre code { + background-color: transparent; + color: inherit; + padding: 0; +} + +/* Code Syntax Highlights (CSS simulated) */ +.kw { color: #f472b6; font-weight: bold; } /* def, class, return, if */ +.fn { color: #60a5fa; } /* function name */ +.str { color: #a7f3d0; } /* strings */ +.num { color: #fbbf24; } /* numbers */ +.cm { color: #64748b; font-style: italic; } /* comments */ +.var { color: #cbd5e1; } /* self, variables */ +.attr { color: #38bdf8; } /* self.attributes */ + +/* Accordion / Details */ +details { + background: #f8fafc; + border: 1px solid var(--border-color); + border-radius: var(--radius-sm); + padding: 12px 16px; + margin: 12px 0; + transition: all 0.2s; +} + +details[open] { + background: #ffffff; + border-color: var(--primary); +} + +summary { + font-weight: 600; + cursor: pointer; + color: var(--primary); + user-select: none; + display: flex; + align-items: center; + gap: 8px; +} + +summary:hover { + color: var(--primary-hover); +} + +.details-content { + margin-top: 12px; + padding-top: 10px; + border-top: 1px solid var(--border-color); +} + +/* Checklists & Tasks */ +.task-list { + list-style: none; + margin: 15px 0; +} + +.task-item { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 10px 14px; + background: #ffffff; + border: 1px solid var(--border-color); + border-radius: var(--radius-sm); + margin-bottom: 8px; + transition: background 0.2s; +} + +.task-item:hover { + background: var(--primary-light); +} + +.task-checkbox { + width: 18px; + height: 18px; + margin-top: 3px; + accent-color: var(--primary); + cursor: pointer; +} + +.task-label { + flex: 1; +} + +/* Grid & Roadmap Cards (for Overview) */ +.roadmap-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 20px; + margin: 25px 0; +} + +.roadmap-card { + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + padding: 20px; + text-decoration: none; + color: var(--text-main); + display: flex; + flex-direction: column; + justify-content: space-between; + transition: all 0.25s ease; + box-shadow: var(--shadow-sm); +} + +.roadmap-card:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-lg); + border-color: var(--primary); +} + +.roadmap-number { + font-size: 0.8rem; + font-weight: 700; + text-transform: uppercase; + color: var(--primary); + background: var(--primary-light); + display: inline-block; + padding: 2px 8px; + border-radius: 12px; + margin-bottom: 10px; + align-self: flex-start; +} + +.roadmap-title { + font-size: 1.2rem; + font-weight: 700; + margin-bottom: 8px; + color: var(--text-main); +} + +.roadmap-desc { + font-size: 0.92rem; + color: var(--text-muted); + margin-bottom: 14px; + flex: 1; +} + +.roadmap-footer { + font-size: 0.85rem; + font-weight: 600; + color: var(--primary); + display: flex; + align-items: center; + gap: 6px; +} + +/* Buttons & Links */ +.btn { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 18px; + background: var(--primary); + color: white; + text-decoration: none; + font-weight: 600; + border-radius: var(--radius-sm); + border: none; + cursor: pointer; + transition: all 0.2s; + box-shadow: var(--shadow-sm); +} + +.btn:hover { + background: var(--primary-hover); + transform: translateY(-1px); +} + +.btn-secondary { + background: #f1f5f9; + color: var(--text-main); + border: 1px solid var(--border-color); +} + +.btn-secondary:hover { + background: #e2e8f0; +} + +.footer-nav { + display: flex; + justify-content: space-between; + margin-top: 35px; + gap: 15px; + flex-wrap: wrap; +} + +/* Responsive */ +@media (max-width: 600px) { + header h1 { font-size: 1.6rem; } + .card { padding: 18px; } + .footer-nav { flex-direction: column; } + .footer-nav .btn { width: 100%; justify-content: center; } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..6578a55 --- /dev/null +++ b/index.html @@ -0,0 +1,166 @@ + + + + + + Python OOP Lernportal: Klassen & Objekte + + + + +
+
+
🐍 Python Nachhilfe & Selbstlern-Kurs
+

Objektorientierte Programmierung (OOP)

+

Lerne Schritt für Schritt, wie du eigene Klassen baust, Methoden nutzt und komplexe Programme strukturierst.

+
+
+ +
+ + +
+

👋 Willkommen zu deiner Übungsreihe!

+

+ Bisher hast du in Python schon viel gelernt: Variablen, Schleifen, Bedingungen, Listen, Dictionaries und Funktionen. + Jetzt machen wir den nächsten großen Schritt in der Softwareentwicklung: Klassen & Objekte! +

+

+ Mit Klassen kannst du zusammengehörige Daten und Aktionen an einem Ort bündeln. Fast jede moderne App (ob Instagram, Spotify oder Computerspiele) basiert auf objektorientierter Programmierung. +

+ +
+
🏗️ Die Grundidee in einem Satz:
+ Eine Klasse ist wie ein Bauplan (oder eine Plätzchen-Ausstechform). Ein Objekt ist das fertige Haus (oder das gebackene Plätzchen), das nach diesem Bauplan entstanden ist! +
+
+ + +
+

🗺️ Dein Lernpfad (Kapitel 01 bis 06)

+ +
+ + +
+

🚀 Wie du vorgehst (Schritt-für-Schritt)

+
    +
  1. Wähle das nächste offene Kapitel aus der Liste oben.
  2. +
  3. Lies die Erklärungen und schau dir die Codebeispiele und „Stolperfallen“ an.
  4. +
  5. Öffne die aufgabe.py im entsprechenden Kapitelordner und löse die TODOs.
  6. +
  7. + Führe im Terminal den automatischen Test aus: +
    +
    + Terminal +
    +
    python3 test_aufgabe.py
    +
    +
  8. +
  9. Wenn alle Tests mit OK durchlaufen, hast du das Kapitel erfolgreich gemeistert! 🎉
  10. +
+ +
+
💡 Gesamten Fortschritt auf einen Blick prüfen:
+ Du kannst jederzeit im Hauptordner folgenden Befehl ausführen, um zu sehen, wie viele Kapitel du schon gelöst hast: +
+
python3 test_all.py
+
+
+
+ + +
+

🩺 Erste Hilfe & Häufige Fragen

+ +
+ ❌ Fehlermeldung: TypeError: ... takes 0 positional arguments but 1 was given +
+

Ursache: Du hast bei einer Methode in deiner Klasse das self im Methodenkopf vergessen!

+

Lösung: Schreibe def meine_methode(self): statt def meine_methode():. Python übergibt bei Methodenaufrufen immer automatisch das Objekt selbst als erstes Argument.

+
+
+ +
+ ❌ Fehlermeldung: AttributeError: 'NoneType' object has no attribute '...' +
+

Ursache: Eine Funktion oder Methode hat nichts zurückgegeben (also None), und du versuchst, auf ein Attribut davon zuzugreifen.

+

Lösung: Prüfe, ob du am Ende der Funktion das return objekt vergessen hast!

+
+
+ +
+ ❌ Der Test schlägt fehl, obwohl ich die richtige Ausgabe in der Konsole sehe? +
+

Ursache: Du hast vermutlich print(...) verwendet, anstatt den Wert mit return ... zurückzugeben.

+

Lösung: Die automatischen Tests prüfen den Rückgabewert (return) deiner Funktionen/Methoden, nicht das, was auf dem Bildschirm ausgegeben wird.

+
+
+
+ +
+ + +