Erweitere Übungsreihe um detaillierte Erklärungen, Stolperfallen-Guides und modernes HTML-Lernportal

This commit is contained in:
Alex Mueller 2026-08-29 12:53:24 +02:00
parent ce35e7d479
commit fbaec82182
19 changed files with 1811 additions and 141 deletions

View file

@ -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. 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 ```python
class Auto: 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**): 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. **Groß- und Kleinschreibung:**
1. Erstelle die Klasse `Haustier`. - Klassen werden per Konvention in **CamelCase** mit großem Anfangsbuchstaben geschrieben: `class Haustier:`, `class BankKonto:`.
2. Erzeuge zwei Haustier-Objekte `bello` und `mimi` mit Eigenschaften. - Variablen und Funktionen schreibt man klein mit Unterstrichen: `mein_haustier = Haustier()`.
3. Schreibe zwei kleine Hilfsfunktionen, die mit deinen Haustier-Objekten arbeiten. 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: ### Testen:
Führe deine Tests aus mit: Führe deine Tests aus mit:
```bash ```bash
python3 test_aufgabe.py 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!

View file

@ -3,13 +3,16 @@ Kapitel 01: Die erste Klasse – Aufgabenblatt
============================================ ============================================
Bearbeite die Aufgaben Schritt für Schritt von TODO 1 bis TODO 5. 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 python3 test_aufgabe.py
""" """
# ============================================================================== # ==============================================================================
# TODO 1: Definiere eine leere Klasse namens "Haustier". # 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: # 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()". # TODO 2: Erstelle eine Funktion namens "erstelle_bello()".
# Diese Funktion soll: #
# 1. Ein neues Haustier-Objekt erzeugen. # Schritt-für-Schritt-Anleitung:
# 2. Dem Objekt folgende Attribute zuweisen: # 1. Erstelle ein neues Haustier-Objekt: z.B. hund = Haustier()
# - name: "Bello" # 2. Setze die drei Attribute mit dem Punkt-Operator:
# - tierart: "Hund" # hund.name = "Bello"
# - alter: 3 # hund.tierart = "Hund"
# 3. Das fertige Objekt zurückgeben (return). # hund.alter = 3
# 3. Gib das erstellte Objekt mit 'return hund' zurück!
# ============================================================================== # ==============================================================================
def erstelle_bello(): def erstelle_bello():
@ -34,13 +38,12 @@ def erstelle_bello():
# ============================================================================== # ==============================================================================
# TODO 3: Erstelle eine Funktion namens "erstelle_mimi()". # TODO 3: Erstelle eine Funktion namens "erstelle_mimi()".
# Diese Funktion soll: #
# 1. Ein neues Haustier-Objekt erzeugen. # Ähnlich wie bei Bello:
# 2. Dem Objekt folgende Attribute zuweisen: # - name = "Mimi"
# - name: "Mimi" # - tierart = "Katze"
# - tierart: "Katze" # - alter = 5
# - alter: 5 # Vergiss nicht das 'return'!
# 3. Das fertige Objekt zurückgeben (return).
# ============================================================================== # ==============================================================================
def erstelle_mimi(): def erstelle_mimi():
@ -50,13 +53,15 @@ def erstelle_mimi():
# ============================================================================== # ==============================================================================
# TODO 4: Schreibe eine Funktion "steckbrief_text(haustier)". # TODO 4: Schreibe eine Funktion "steckbrief_text(haustier)".
#
# Die Funktion bekommt ein Haustier-Objekt als Parameter übergeben. # Die Funktion bekommt ein Haustier-Objekt als Parameter übergeben.
# Sie soll einen formatierten Text im folgenden Format zurückgeben: # Sie soll einen formatierten Text im folgenden Format zurückgeben:
# "{name} ist ein(e) {tierart} und ist {alter} Jahre alt." # "{name} ist ein(e) {tierart} und ist {alter} Jahre alt."
# #
# Tipp: Nutze einen f-String: f"{haustier.name} ist ein(e) {haustier.tierart} ..."
#
# Beispiel: # Beispiel:
# Wenn bello übergeben wird, soll zurückgegeben werden: # Wenn bello übergeben wird: "Bello ist ein(e) Hund und ist 3 Jahre alt."
# "Bello ist ein(e) Hund und ist 3 Jahre alt."
# ============================================================================== # ==============================================================================
def steckbrief_text(haustier): def steckbrief_text(haustier):
@ -66,9 +71,16 @@ def steckbrief_text(haustier):
# ============================================================================== # ==============================================================================
# TODO 5: Schreibe eine Funktion "aelteres_tier(tier1, tier2)". # TODO 5: Schreibe eine Funktion "aelteres_tier(tier1, tier2)".
#
# Die Funktion bekommt zwei Haustier-Objekte übergeben. # Die Funktion bekommt zwei Haustier-Objekte übergeben.
# Sie soll dasjenige Haustier-Objekt zurückgeben, dessen 'alter' größer ist. # Sie soll dasjenige Haustier-Objekt zurückgeben, dessen 'alter' größer ist.
# Wenn beide Tiere gleich alt sind, soll tier1 zurückgegeben werden. # 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): def aelteres_tier(tier1, tier2):
@ -89,6 +101,6 @@ if __name__ == "__main__":
print(steckbrief_text(bello)) print(steckbrief_text(bello))
print(steckbrief_text(mimi)) print(steckbrief_text(mimi))
aelter = aelteres_tier(bello, 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: else:
print("Hinweis: Implementiere erst die TODOs, um die Ausgabe zu sehen!") print("💡 Hinweis: Implementiere erst die TODOs, um die Ausgabe zu sehen!")

View file

@ -0,0 +1,192 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kapitel 01: Einstieg in Klassen</title>
<link rel="stylesheet" href="../assets/style.css">
</head>
<body>
<header>
<div class="header-container">
<nav class="nav-links">
<a href="../index.html">🏠 Zur Kursübersicht</a>
<span>&rsaquo;</span>
<span style="color: white; font-weight: 600;">Kapitel 01</span>
</nav>
<div class="header-badge">Kapitel 01</div>
<h1>Die erste Klasse: Bauplan vs. Objekt 🐶</h1>
<p>Verstehe den Unterschied zwischen einer Klasse und einer Instanz und baue deine ersten Haustier-Objekte.</p>
</div>
</header>
<main class="container">
<!-- Theorie Card -->
<section class="card">
<h2>1. Die wichtigste Idee: Bauplan vs. Haus</h2>
<p>
In Python ist alles, was du bisher kennengelernt hast (Zahlen, Strings, Listen, Dictionaries), im Grunde ein <strong>Objekt</strong>. Doch jetzt lernst du, wie du <strong>deine ganz eigenen Datentypen</strong> erfindest!
</p>
<div class="box box-analogie">
<div class="box-title">🏗️ Die Plätzchenform-Analogie:</div>
<p>Stell dir das Backen vor:</p>
<ul style="margin-left: 20px;">
<li>Die <strong>Klasse (<code>class</code>)</strong> ist die <strong>Ausstechform</strong>. Sie legt fest: Jedes Plätzchen hat einen Kopf, zwei Arme und zwei Beine. Die Form selbst kann man aber nicht essen.</li>
<li>Das <strong>Objekt (Instanz)</strong> ist das <strong>echte Plätzchen</strong>, das du aus dem Teig ausstichst. Ein Plätzchen bekommt Schokoglasur, das andere Zuckerstreusel. Beide stammen aus derselben Form, sind aber eigenständig!</li>
</ul>
</div>
<h3>Warum nicht einfach Dictionaries?</h3>
<p>Ein Dictionary hat keine feste Struktur – man vertippt sich leicht:</p>
<div class="code-container">
<div class="code-header">
<div class="code-dots">
<span class="code-dot dot-red"></span>
<span class="code-dot dot-yellow"></span>
<span class="code-dot dot-green"></span>
</div>
<span>Python (Problem mit Dictionaries)</span>
</div>
<pre><span class="var">tier1</span> = {<span class="str">"name"</span>: <span class="str">"Bello"</span>, <span class="str">"tierart"</span>: <span class="str">"Hund"</span>, <span class="str">"alter"</span>: <span class="num">3</span>}
<span class="var">tier2</span> = {<span class="str">"name"</span>: <span class="str">"Mimi"</span>, <span class="str">"art"</span>: <span class="str">"Katze"</span>, <span class="str">"alter"</span>: <span class="num">5</span>} <span class="cm"># Ups! 'art' statt 'tierart'!</span>
<span class="cm"># Ein Programm würde hier abstürzen, weil tier2["tierart"] nicht existiert!</span></pre>
</div>
<p>Mit einer <strong>Klasse</strong> definierst du eine klare, wiederverwendbare Struktur.</p>
</section>
<!-- Syntax Card -->
<section class="card">
<h2>2. Wie definiert und nutzt man eine Klasse?</h2>
<p>Eine einfache Klasse erstellst du mit dem Schlüsselwort <code>class</code>:</p>
<div class="code-container">
<div class="code-header">
<div class="code-dots">
<span class="code-dot dot-red"></span>
<span class="code-dot dot-yellow"></span>
<span class="code-dot dot-green"></span>
</div>
<span>Python</span>
</div>
<pre><span class="cm"># 1. Bauplan definieren:</span>
<span class="kw">class</span> <span class="fn">Auto</span>:
<span class="kw">pass</span> <span class="cm"># pass bedeutet: Hier steht erst mal kein weiterer Code</span>
<span class="cm"># 2. Ein Objekt erzeugen (Instanziierung):</span>
<span class="var">mein_auto</span> = <span class="fn">Auto</span>()
<span class="cm"># 3. Eigenschaften (Attribute) mit dem Punkt (.) setzen:</span>
<span class="var">mein_auto</span>.<span class="attr">farbe</span> = <span class="str">"Rot"</span>
<span class="var">mein_auto</span>.<span class="attr">marke</span> = <span class="str">"VW"</span>
<span class="var">mein_auto</span>.<span class="attr">ps</span> = <span class="num">110</span>
<span class="cm"># 4. Werte wieder abfragen:</span>
<span class="fn">print</span>(<span class="var">mein_auto</span>.<span class="attr">farbe</span>) <span class="cm"># Gibt "Rot" aus</span></pre>
</div>
<div class="box box-warning">
<div class="box-title">⚠️ Häufige Stolperfalle: Klassenname Großschreiben!</div>
In Python schreibt man Klassennamen standardmäßig in <strong>CamelCase</strong> mit großem Anfangsbuchstaben (z.B. <code>Haustier</code>, <code>BankKonto</code>), während Variablen und Funktionen klein geschrieben werden (z.B. <code>mein_haustier</code>, <code>erstelle_tier()</code>).
</div>
</section>
<!-- Aufgaben Card -->
<section class="card">
<h2>3. Deine Aufgaben in <code>aufgabe.py</code></h2>
<p>Öffne die Datei <code>01_einstieg_klassen/aufgabe.py</code> und bearbeite die nummerierten TODOs:</p>
<ul class="task-list">
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t1">
<label for="t1" class="task-label">
<strong>TODO 1:</strong> Definiere die Klasse <code>Haustier</code> mit <code>pass</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t2">
<label for="t2" class="task-label">
<strong>TODO 2:</strong> Funktion <code>erstelle_bello()</code>: Erzeuge ein Haustier-Objekt mit <code>name="Bello"</code>, <code>tierart="Hund"</code>, <code>alter=3</code> und gib es mit <code>return</code> zurück.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t3">
<label for="t3" class="task-label">
<strong>TODO 3:</strong> Funktion <code>erstelle_mimi()</code>: Erzeuge ein Haustier-Objekt mit <code>name="Mimi"</code>, <code>tierart="Katze"</code>, <code>alter=5</code> und gib es zurück.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t4">
<label for="t4" class="task-label">
<strong>TODO 4:</strong> Funktion <code>steckbrief_text(haustier)</code>: Gibt einen f-String zurück: <code>"{haustier.name} ist ein(e) {haustier.tierart} und ist {haustier.alter} Jahre alt."</code>
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t5">
<label for="t5" class="task-label">
<strong>TODO 5:</strong> Funktion <code>aelteres_tier(tier1, tier2)</code>: Vergleicht <code>tier1.alter</code> und <code>tier2.alter</code> und gibt das ältere Haustier-Objekt zurück (bei Gleichstand <code>tier1</code>).
</label>
</li>
</ul>
<h3>💡 Lösungshinweise (Aufklappen bei Bedarf)</h3>
<details>
<summary>Tipp zu TODO 1 & 2 (Objekt erstellen und befüllen)</summary>
<div class="details-content">
<div class="code-container">
<pre><span class="kw">class</span> <span class="fn">Haustier</span>:
<span class="kw">pass</span>
<span class="kw">def</span> <span class="fn">erstelle_bello</span>():
<span class="var">tier</span> = <span class="fn">Haustier</span>() <span class="cm"># Neues Objekt erzeugen</span>
<span class="var">tier</span>.<span class="attr">name</span> = <span class="str">"Bello"</span>
<span class="var">tier</span>.<span class="attr">tierart</span> = <span class="str">"Hund"</span>
<span class="var">tier</span>.<span class="attr">alter</span> = <span class="num">3</span>
<span class="kw">return</span> <span class="var">tier</span> <span class="cm"># WICHTIG: Das fertige Objekt zurückgeben!</span></pre>
</div>
</div>
</details>
<details>
<summary>Tipp zu TODO 5 (Tiere vergleichen)</summary>
<div class="details-content">
<p>Du kannst mit einem einfachen <code>if</code> auf die Attribute der übergebenen Objekte zugreifen:</p>
<div class="code-container">
<pre><span class="kw">if</span> <span class="var">tier2</span>.<span class="attr">alter</span> > <span class="var">tier1</span>.<span class="attr">alter</span>:
<span class="kw">return</span> <span class="var">tier2</span>
<span class="kw">else</span>:
<span class="kw">return</span> <span class="var">tier1</span></pre>
</div>
</div>
</details>
</section>
<!-- Testen & Footer Navigation -->
<section class="card">
<h2>4. Lösung überprüfen</h2>
<p>Führe im Terminal im Ordner <code>01_einstieg_klassen</code> folgenden Befehl aus:</p>
<div class="code-container">
<div class="code-header">
<span>Terminal</span>
</div>
<pre><code>python3 test_aufgabe.py</code></pre>
</div>
<div class="footer-nav">
<a href="../index.html" class="btn btn-secondary">&larr; Zurück zur Übersicht</a>
<a href="../02_init_und_self/index.html" class="btn">Weiter zu Kapitel 02 &rarr;</a>
</div>
</section>
</main>
</body>
</html>

View file

@ -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 ## 🎯 Deine Aufgabe: Das Bankkonto
Öffne `aufgabe.py`. Du programmierst eine Klasse `Bankkonto`: Öffne `aufgabe.py`. Du programmierst eine Klasse `Bankkonto`:

View file

@ -10,9 +10,11 @@ class Bankkonto:
# TODO 1: Schreibe die __init__-Methode. # TODO 1: Schreibe die __init__-Methode.
# Parameter: self, inhaber (String), kontostand (Float, Standardwert 0.0) # Parameter: self, inhaber (String), kontostand (Float, Standardwert 0.0)
# #
# Speichere die Parameter als Attribute an 'self': # Anleitung:
# - self.inhaber # 1. Definiere: def __init__(self, inhaber, kontostand=0.0):
# - self.kontostand # 2. Speichere die übergebenen Werte an self:
# self.inhaber = inhaber
# self.kontostand = kontostand
# ========================================================================== # ==========================================================================
def __init__(self, inhaber, kontostand=0.0): def __init__(self, inhaber, kontostand=0.0):
# Schreibe hier deinen Code für TODO 1: # Schreibe hier deinen Code für TODO 1:
@ -20,13 +22,14 @@ class Bankkonto:
# ========================================================================== # ==========================================================================
# TODO 2: Schreibe die Methode "einzahlen(self, betrag)". # TODO 2: Schreibe die Methode "einzahlen(self, betrag)".
#
# Regeln: # Regeln:
# 1. Wenn 'betrag' größer als 0 ist: # 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). # - Gib True zurück (Einzahlung erfolgreich).
# 2. Wenn 'betrag' kleiner oder gleich 0 ist: # 2. Wenn 'betrag' kleiner oder gleich 0 ist (z.B. 0 oder -5):
# - Verändere den Kontostand nicht. # - Ändere nichts am Kontostand.
# - Gib False zurück (ungültiger Betrag). # - Gib False zurück.
# ========================================================================== # ==========================================================================
def einzahlen(self, betrag): def einzahlen(self, betrag):
# Schreibe hier deinen Code für TODO 2: # Schreibe hier deinen Code für TODO 2:
@ -34,13 +37,12 @@ class Bankkonto:
# ========================================================================== # ==========================================================================
# TODO 3: Schreibe die Methode "auszahlen(self, betrag)". # TODO 3: Schreibe die Methode "auszahlen(self, betrag)".
#
# Regeln: # Regeln:
# 1. Der Betrag muss größer als 0 sein UND es muss genug Geld auf dem Konto # 1. Prüfe mit 'if', ob 'betrag > 0' UND 'self.kontostand >= betrag':
# sein (self.kontostand >= betrag). # - Wenn ja: Ziehe den Betrag ab (self.kontostand -= betrag)
# - Ziehe 'betrag' von 'self.kontostand' ab. # - Gib True zurück.
# - Gib True zurück (Auszahlung erfolgreich). # 2. Wenn nicht (nicht genug Geld da oder ungültiger Betrag):
# 2. Andernfalls (nicht genug Geld oder ungültiger Betrag):
# - Verändere den Kontostand nicht.
# - Gib False zurück. # - Gib False zurück.
# ========================================================================== # ==========================================================================
def auszahlen(self, betrag): def auszahlen(self, betrag):
@ -49,10 +51,12 @@ class Bankkonto:
# ========================================================================== # ==========================================================================
# TODO 4: Schreibe die Methode "info_text(self)". # 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" # "Konto von {inhaber}: {kontostand:.2f} Euro"
# #
# Beispiel: Bei inhaber="Mia" und kontostand=25.5 -> "Konto von Mia: 25.50 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): def info_text(self):
# Schreibe hier deinen Code für TODO 4: # Schreibe hier deinen Code für TODO 4:
@ -61,17 +65,22 @@ class Bankkonto:
# ============================================================================== # ==============================================================================
# Hauptprogramm zum Ausprobieren: # Hauptprogramm zum Ausprobieren:
# (Direkt ausführen mit 'python3 aufgabe.py')
# ============================================================================== # ==============================================================================
if __name__ == "__main__": if __name__ == "__main__":
print("--- Teste dein Bankkonto ---") print("--- Teste dein Bankkonto ---")
konto = Bankkonto("Mia", 20.0) 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) erfolg = konto.einzahlen(15.5)
print(f"15.50€ eingezahlt? {erfolg} -> Neuer Stand: {konto.kontostand}€") print(f"15.50€ eingezahlt? {erfolg} -> Neuer Stand: {konto.kontostand:.2f}€")
erfolg = konto.auszahlen(50.0) erfolg = konto.auszahlen(50.0)
print(f"50.00€ ausgezahlt? {erfolg} (sollte False sein) -> Stand: {konto.kontostand}€") print(f"50.00€ ausgezahlt? {erfolg} (sollte False sein) -> Stand: {konto.kontostand:.2f}€")
erfolg = konto.auszahlen(10.0) erfolg = konto.auszahlen(10.0)
print(f"10.00€ ausgezahlt? {erfolg} -> Stand: {konto.kontostand}€") print(f"10.00€ ausgezahlt? {erfolg} -> Stand: {konto.kontostand:.2f}€")
else:
print("💡 Hinweis: Implementiere erst die TODOs, um die Ausgabe zu sehen!")

171
02_init_und_self/index.html Normal file
View file

@ -0,0 +1,171 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kapitel 02: Konstruktor __init__ und self</title>
<link rel="stylesheet" href="../assets/style.css">
</head>
<body>
<header>
<div class="header-container">
<nav class="nav-links">
<a href="../index.html">🏠 Zur Kursübersicht</a>
<span>&rsaquo;</span>
<a href="../01_einstieg_klassen/index.html">Kapitel 01</a>
<span>&rsaquo;</span>
<span style="color: white; font-weight: 600;">Kapitel 02</span>
</nav>
<div class="header-badge">Kapitel 02</div>
<h1>Der Konstruktor <code>__init__</code> &amp; das geheimnisvolle <code>self</code> 💳</h1>
<p>Lerne, wie Objekte direkt beim Erstellen startklar gemacht werden und baue eine Taschengeld-Konto App.</p>
</div>
</header>
<main class="container">
<!-- Theorie Card -->
<section class="card">
<h2>1. Warum brauchen wir <code>__init__</code>?</h2>
<p>
In Kapitel 01 mussten wir jedes Attribut mühsam von Hand setzen:
</p>
<div class="code-container">
<pre><span class="var">bello</span> = <span class="fn">Haustier</span>()
<span class="var">bello</span>.<span class="attr">name</span> = <span class="str">"Bello"</span>
<span class="var">bello</span>.<span class="attr">alter</span> = <span class="num">3</span> <span class="cm"># Was passiert, wenn man hier das Alter vergisst? -> Programm stürzt ab!</span></pre>
</div>
<p>
Deshalb gibt es den <strong>Konstruktor</strong> <code>__init__</code>. Er wird von Python <strong>automatisch aufgerufen</strong>, sobald ein neues Objekt erstellt wird!
</p>
<div class="box box-analogie">
<div class="box-title">🏗️ Die Geburtsurkunde-Analogie:</div>
Wenn ein neues Objekt "geboren" wird, muss es sofort seine grundlegenden Daten erhalten (Name, Startwert). <code>__init__</code> ist wie das Ausfüllen des Datenblattes bei der Geburt – danach ist das Objekt sofort einsatzbereit!
</div>
</section>
<!-- self Erklärung -->
<section class="card">
<h2>2. Was bedeutet <code>self</code>?</h2>
<p>
Das Wörtchen <code>self</code> steht auf Deutsch für <strong>"ich selbst"</strong> bzw. <strong>"dieses konkrete Objekt hier"</strong>.
</p>
<div class="code-container">
<div class="code-header">
<div class="code-dots">
<span class="code-dot dot-red"></span>
<span class="code-dot dot-yellow"></span>
<span class="code-dot dot-green"></span>
</div>
<span>Python</span>
</div>
<pre><span class="kw">class</span> <span class="fn">Bankkonto</span>:
<span class="kw">def</span> <span class="fn">__init__</span>(<span class="var">self</span>, <span class="var">inhaber</span>, <span class="var">kontostand</span>=<span class="num">0.0</span>):
<span class="var">self</span>.<span class="attr">inhaber</span> = <span class="var">inhaber</span> <span class="cm"># Speichere 'inhaber' im Attribut DIESES Objekts</span>
<span class="var">self</span>.<span class="attr">kontostand</span> = <span class="var">kontostand</span> <span class="cm"># Speichere 'kontostand' in MEINEM Speicher</span>
<span class="cm"># Verwendung:</span>
<span class="var">konto_anna</span> = <span class="fn">Bankkonto</span>(<span class="str">"Anna"</span>, <span class="num">50.0</span>)
<span class="var">konto_ben</span> = <span class="fn">Bankkonto</span>(<span class="str">"Ben"</span>) <span class="cm"># Verwendet den Standardwert 0.0</span></pre>
</div>
<div class="box box-tipp">
<div class="box-title">💡 Warum <code>self.inhaber = inhaber</code>?</div>
Links vom Gleichheitszeichen steht <code>self.inhaber</code> – das ist die Variable, die <strong>am Objekt dauerhaft gespeichert</strong> bleibt. Rechts steht <code>inhaber</code> – das ist nur der temporäre Übergabeparameter der Funktion.
</div>
</section>
<!-- Stolperfallen -->
<section class="card">
<h2>3. Häufige Stolperfallen beim Konstruktor</h2>
<div class="box box-warning">
<div class="box-title">⚠️ Fehler 1: Doppel-Unterstrich vs. Einzel-Unterstrich</div>
Es heißt <code>__init__</code> mit <strong>zwei Unterstrichen</strong> davor und danach (Dunder), NICHT <code>_init_</code>! Wenn du nur einen Unterstrich schreibst, ignoriert Python die Methode einfach.
</div>
<div class="box box-danger">
<div class="box-title">⚠️ Fehler 2: <code>self</code> im Methodenkopf vergessen</div>
Jede Methode in einer Klasse <strong>muss</strong> als allerersten Parameter <code>self</code> haben:
<br><code>def einzahlen(self, betrag):</code> &nbsp;&check;
<br><code>def einzahlen(betrag):</code> &nbsp;&cross; (Führt zu <code>TypeError</code>!)
</div>
</section>
<!-- Aufgaben -->
<section class="card">
<h2>4. Deine Aufgaben in <code>aufgabe.py</code></h2>
<p>Öffne die Datei <code>02_init_und_self/aufgabe.py</code>:</p>
<ul class="task-list">
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t2_1">
<label for="t2_1" class="task-label">
<strong>TODO 1:</strong> Schreibe <code>__init__(self, inhaber, kontostand=0.0)</code> und binde <code>inhaber</code> und <code>kontostand</code> an <code>self</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t2_2">
<label for="t2_2" class="task-label">
<strong>TODO 2:</strong> Methode <code>einzahlen(self, betrag)</code>: Wenn <code>betrag > 0</code>, erhöhe Kontostand und gib <code>True</code> zurück, sonst <code>False</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t2_3">
<label for="t2_3" class="task-label">
<strong>TODO 3:</strong> Methode <code>auszahlen(self, betrag)</code>: Wenn <code>betrag > 0</code> und <code>self.kontostand >= betrag</code>, ziehe Geld ab und gib <code>True</code> zurück, sonst <code>False</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t2_4">
<label for="t2_4" class="task-label">
<strong>TODO 4:</strong> Methode <code>info_text(self)</code>: Gibt f-String zurück: <code>"Konto von {self.inhaber}: {self.kontostand:.2f} Euro"</code>.
</label>
</li>
</ul>
<h3>💡 Lösungshinweise (Aufklappen bei Bedarf)</h3>
<details>
<summary>Tipp zu TODO 3 (Auszahlen mit Absicherung)</summary>
<div class="details-content">
<div class="code-container">
<pre><span class="kw">def</span> <span class="fn">auszahlen</span>(<span class="var">self</span>, <span class="var">betrag</span>):
<span class="kw">if</span> <span class="var">betrag</span> > <span class="num">0</span> <span class="kw">and</span> <span class="var">self</span>.<span class="attr">kontostand</span> >= <span class="var">betrag</span>:
<span class="var">self</span>.<span class="attr">kontostand</span> -= <span class="var">betrag</span>
<span class="kw">return</span> <span class="kw">True</span>
<span class="kw">return</span> <span class="kw">False</span></pre>
</div>
</div>
</details>
</section>
<!-- Testen & Footer Navigation -->
<section class="card">
<h2>5. Lösung überprüfen</h2>
<p>Führe im Terminal im Ordner <code>02_init_und_self</code> folgenden Befehl aus:</p>
<div class="code-container">
<div class="code-header">
<span>Terminal</span>
</div>
<pre><code>python3 test_aufgabe.py</code></pre>
</div>
<div class="footer-nav">
<a href="../01_einstieg_klassen/index.html" class="btn btn-secondary">&larr; Zurück zu Kapitel 01</a>
<a href="../03_methoden_und_verhalten/index.html" class="btn">Weiter zu Kapitel 03 &rarr;</a>
</div>
</section>
</main>
</body>
</html>

View file

@ -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 ## 🎯 Deine Aufgabe: Das RPG-Duell
In `aufgabe.py` erstellst du eine Klasse `Held` für ein Rollenspiel: In `aufgabe.py` erstellst du eine Klasse `Held` für ein Rollenspiel:

View file

@ -8,7 +8,8 @@ In dieser Aufgabe programmierst du eine Klasse 'Held' für ein Rollenspiel.
class Held: class Held:
# ========================================================================== # ==========================================================================
# TODO 1: Konstruktor __init__(self, name, leben=100, angriffskraft=15) # TODO 1: Konstruktor __init__(self, name, leben=100, angriffskraft=15)
# Speichere: #
# Speichere die folgenden Attribute an self:
# - self.name = name # - self.name = name
# - self.leben = leben # - self.leben = leben
# - self.max_leben = leben (das maximale Leben entspricht dem Startleben) # - self.max_leben = leben (das maximale Leben entspricht dem Startleben)
@ -21,14 +22,15 @@ class Held:
# ========================================================================== # ==========================================================================
# TODO 2: Methode "schaden_erleiden(self, schaden)" # TODO 2: Methode "schaden_erleiden(self, schaden)"
# Regeln: #
# 1. Wenn der Held bereits besiegt ist (not self.ist_am_leben) oder schaden <= 0: # Schritt-für-Schritt:
# - Es passiert nichts, gib 0 zurück. # 1. Wenn der Held schon tot ist (not self.ist_am_leben) oder schaden <= 0:
# 2. Ziehe 'schaden' von 'self.leben' ab. # - return 0
# 3. Wenn 'self.leben' <= 0 wird: # 2. Berechne den Schaden und ziehe ihn von self.leben ab.
# - Setze 'self.leben' genau auf 0. # 3. Wenn self.leben <= 0 wird:
# - Setze 'self.ist_am_leben' auf False. # - setze self.leben = 0
# 4. Gib den erlittenen Schaden (int/float) zurück. # - setze self.ist_am_leben = False
# 4. Gib den erlittenen Schaden zurück (z.B. return schaden).
# ========================================================================== # ==========================================================================
def schaden_erleiden(self, schaden): def schaden_erleiden(self, schaden):
# Schreibe hier deinen Code für TODO 2: # Schreibe hier deinen Code für TODO 2:
@ -36,14 +38,16 @@ class Held:
# ========================================================================== # ==========================================================================
# TODO 3: Methode "heilen(self, heilung)" # TODO 3: Methode "heilen(self, heilung)"
# Regeln: #
# 1. Wenn der Held besiegt ist (not self.ist_am_leben) oder heilung <= 0: # Schritt-für-Schritt:
# - Tote Helden können nicht geheilt werden! Gib 0 zurück. # 1. Wenn der Held tot ist (not self.ist_am_leben) oder heilung <= 0:
# 2. Erhöhe das Leben um 'heilung', aber ACHTUNG: # - return 0 (Tote können nicht geheilt werden!)
# - 'self.leben' darf niemals größer als 'self.max_leben' werden! # 2. Merke dir das aktuelle Leben: alter_wert = self.leben
# - Beispiel: Wenn leben=90, max_leben=100 und heilung=20: # 3. Erhöhe das Leben, aber maximal bis self.max_leben:
# Das Leben wird 100 (nicht 110) und die tatsächliche Heilung war 10. # self.leben = min(self.max_leben, self.leben + heilung)
# 3. Gib die tatsächlich geheilte Menge an Lebenspunkten zurück. # 4. Berechne, wie viel tatsächlich geheilt wurde:
# tatsaechlich_geheilt = self.leben - alter_wert
# 5. Gib tatsaechlich_geheilt zurück.
# ========================================================================== # ==========================================================================
def heilen(self, heilung): def heilen(self, heilung):
# Schreibe hier deinen Code für TODO 3: # Schreibe hier deinen Code für TODO 3:
@ -52,13 +56,15 @@ class Held:
# ========================================================================== # ==========================================================================
# TODO 4: Methode "angreifen(self, gegner)" # TODO 4: Methode "angreifen(self, gegner)"
# Parameter 'gegner' ist ein anderes Held-Objekt! # 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: # 2. Wenn ja:
# - Rufe auf dem 'gegner' die Methode 'schaden_erleiden' auf mit self.angriffskraft als Wert. # - Rufe auf dem Gegner schaden_erleiden auf:
# - Gib True zurück. # gegner.schaden_erleiden(self.angriffskraft)
# 3. Wenn einer von beiden (oder beide) nicht mehr am Leben ist: # - Gib True zurück (Angriff erfolgreich).
# - Greife nicht an und gib False zurück. # 3. Wenn einer von beiden besiegt ist:
# - Gib False zurück.
# ========================================================================== # ==========================================================================
def angreifen(self, gegner): def angreifen(self, gegner):
# Schreibe hier deinen Code für TODO 4: # Schreibe hier deinen Code für TODO 4:
@ -66,6 +72,7 @@ class Held:
# ========================================================================== # ==========================================================================
# TODO 5: Methode "status_text(self)" # TODO 5: Methode "status_text(self)"
#
# Gibt einen formatierten String zurück: # Gibt einen formatierten String zurück:
# Wenn am Leben: # Wenn am Leben:
# "[{self.name}] HP: {self.leben}/{self.max_leben} | Kraft: {self.angriffskraft} | Status: Lebendig" # "[{self.name}] HP: {self.leben}/{self.max_leben} | Kraft: {self.angriffskraft} | Status: Lebendig"
@ -79,23 +86,27 @@ class Held:
# ============================================================================== # ==============================================================================
# Kampfsimulator zum Ausprobieren: # Kampfsimulator zum Ausprobieren:
# (Ausführen mit 'python3 aufgabe.py')
# ============================================================================== # ==============================================================================
if __name__ == "__main__": if __name__ == "__main__":
print("⚔️ --- ARENA DER HELDEN --- ⚔️") print("⚔️ --- ARENA DER HELDEN --- ⚔️")
ritter = Held("Arthur", leben=80, angriffskraft=20) ritter = Held("Arthur", leben=80, angriffskraft=20)
drache = Held("Feuerspei", leben=120, angriffskraft=25) drache = Held("Feuerspei", leben=120, angriffskraft=25)
print(ritter.status_text()) if hasattr(ritter, "name") and ritter.name:
print(drache.status_text()) print(ritter.status_text())
print(drache.status_text())
print("\n--- Runde 1 ---") print("\n--- Runde 1 ---")
ritter.angreifen(drache) ritter.angreifen(drache)
print(drache.status_text()) print(drache.status_text())
drache.angreifen(ritter) drache.angreifen(ritter)
print(ritter.status_text()) print(ritter.status_text())
print("\n--- Arthur trinkt einen Heiltrank ---") print("\n--- Arthur trinkt einen Heiltrank ---")
geheilt = ritter.heilen(15) geheilt = ritter.heilen(15)
print(f"Arthur heilt sich um {geheilt} HP!") print(f"Arthur heilt sich um {geheilt} HP!")
print(ritter.status_text()) print(ritter.status_text())
else:
print("💡 Hinweis: Implementiere erst die TODOs, um die Arena zu starten!")

View file

@ -0,0 +1,152 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kapitel 03: Methoden und Verhalten</title>
<link rel="stylesheet" href="../assets/style.css">
</head>
<body>
<header>
<div class="header-container">
<nav class="nav-links">
<a href="../index.html">🏠 Zur Kursübersicht</a>
<span>&rsaquo;</span>
<a href="../02_init_und_self/index.html">Kapitel 02</a>
<span>&rsaquo;</span>
<span style="color: white; font-weight: 600;">Kapitel 03</span>
</nav>
<div class="header-badge">Kapitel 03</div>
<h1>Methoden &amp; Verhalten: Wenn Objekte lebendig werden ⚔️</h1>
<p>Lass Objekte aktiv handeln und miteinander interagieren in einem rundenbasierten RPG-Heldenduell.</p>
</div>
</header>
<main class="container">
<!-- Theorie Card -->
<section class="card">
<h2>1. Was sind Methoden?</h2>
<p>
Bisher haben unsere Klassen vor allem Daten gespeichert. Aber in der objektorientierten Programmierung haben Objekte auch <strong>Verhalten</strong>. Funktionen, die zu einer Klasse gehören, nennt man <strong>Methoden</strong>.
</p>
<div class="code-container">
<div class="code-header">
<div class="code-dots">
<span class="code-dot dot-red"></span>
<span class="code-dot dot-yellow"></span>
<span class="code-dot dot-green"></span>
</div>
<span>Python</span>
</div>
<pre><span class="kw">class</span> <span class="fn">Zauberer</span>:
<span class="kw">def</span> <span class="fn">__init__</span>(<span class="var">self</span>, <span class="var">name</span>, <span class="var">mana</span>=<span class="num">50</span>):
<span class="var">self</span>.<span class="attr">name</span> = <span class="var">name</span>
<span class="var">self</span>.<span class="attr">mana</span> = <span class="var">mana</span>
<span class="cm"># Eine Methode gehört dem Zauberer:</span>
<span class="kw">def</span> <span class="fn">zauberspruch_wirken</span>(<span class="var">self</span>, <span class="var">kosten</span>):
<span class="kw">if</span> <span class="var">self</span>.<span class="attr">mana</span> >= <span class="var">kosten</span>:
<span class="var">self</span>.<span class="attr">mana</span> -= <span class="var">kosten</span>
<span class="kw">return</span> <span class="kw">True</span>
<span class="kw">return</span> <span class="kw">False</span></pre>
</div>
</section>
<!-- Interaktion zwischen Objekten -->
<section class="card">
<h2>2. Wenn zwei Objekte miteinander interagieren</h2>
<p>
Besonders spannend wird es, wenn eine Methode eines Objekts ein <strong>anderes Objekt als Parameter</strong> bekommt (z.B. <code>ritter.angreifen(drache)</code>).
</p>
<div class="box box-analogie">
<div class="box-title">🤝 Die Duell-Analogie:</div>
In <code>angreifen(self, gegner)</code> ist <code>self</code> der Angreifer (z.B. Arthur) und <code>gegner</code> ist das Ziel (z.B. der Drache). Arthur ruft <code>gegner.schaden_erleiden(self.angriffskraft)</code> auf. So beeinflussen sich die Objekte gegenseitig!
</div>
<div class="box box-warning">
<div class="box-title">⚠️ Wichtig: Logische Grenzen einhalten (Clamping)!</div>
Lebenspunkte dürfen niemals unter <code>0</code> fallen (ein besiegter Held hat 0 HP, nicht -15 HP) und bei Heilung niemals über <code>max_leben</code> steigen.
</div>
</section>
<!-- Aufgaben Card -->
<section class="card">
<h2>3. Deine Aufgaben in <code>aufgabe.py</code></h2>
<ul class="task-list">
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t3_1">
<label for="t3_1" class="task-label">
<strong>TODO 1:</strong> <code>__init__(self, name, leben=100, angriffskraft=15)</code> mit <code>max_leben = leben</code> und <code>ist_am_leben = True</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t3_2">
<label for="t3_2" class="task-label">
<strong>TODO 2:</strong> Methode <code>schaden_erleiden(self, schaden)</code>: Zieht Schaden ab. Fällt das Leben auf/unter 0 -> setze <code>leben = 0</code> und <code>ist_am_leben = False</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t3_3">
<label for="t3_3" class="task-label">
<strong>TODO 3:</strong> Methode <code>heilen(self, heilung)</code>: Erhöht Leben maximal bis <code>max_leben</code> (nur wenn noch am Leben).
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t3_4">
<label for="t3_4" class="task-label">
<strong>TODO 4:</strong> Methode <code>angreifen(self, gegner)</code>: Wenn beide lebendig sind, rufe <code>gegner.schaden_erleiden(self.angriffskraft)</code> auf.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t3_5">
<label for="t3_5" class="task-label">
<strong>TODO 5:</strong> Methode <code>status_text(self)</code>: Gibt formatierten Status zurück.
</label>
</li>
</ul>
<h3>💡 Lösungshinweise (Aufklappen bei Bedarf)</h3>
<details>
<summary>Tipp zu TODO 3 (Heilung begrenzen)</summary>
<div class="details-content">
<div class="code-container">
<pre><span class="kw">def</span> <span class="fn">heilen</span>(<span class="var">self</span>, <span class="var">heilung</span>):
<span class="kw">if</span> <span class="kw">not</span> <span class="var">self</span>.<span class="attr">ist_am_leben</span> <span class="kw">or</span> <span class="var">heilung</span> &lt;= <span class="num">0</span>:
<span class="kw">return</span> <span class="num">0</span>
<span class="var">alter_wert</span> = <span class="var">self</span>.<span class="attr">leben</span>
<span class="var">self</span>.<span class="attr">leben</span> = <span class="fn">min</span>(<span class="var">self</span>.<span class="attr">max_leben</span>, <span class="var">self</span>.<span class="attr">leben</span> + <span class="var">heilung</span>)
<span class="kw">return</span> <span class="var">self</span>.<span class="attr">leben</span> - <span class="var">alter_wert</span> <span class="cm"># Wie viel wurde wirklich geheilt?</span></pre>
</div>
</div>
</details>
</section>
<!-- Testen & Footer Navigation -->
<section class="card">
<h2>4. Lösung überprüfen</h2>
<p>Führe im Terminal im Ordner <code>03_methoden_und_verhalten</code> folgenden Befehl aus:</p>
<div class="code-container">
<div class="code-header">
<span>Terminal</span>
</div>
<pre><code>python3 test_aufgabe.py</code></pre>
</div>
<div class="footer-nav">
<a href="../02_init_und_self/index.html" class="btn btn-secondary">&larr; Zurück zu Kapitel 02</a>
<a href="../04_str_und_darstellung/index.html" class="btn">Weiter zu Kapitel 04 &rarr;</a>
</div>
</section>
</main>
</body>
</html>

View file

@ -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 ## 🎯 Deine Aufgabe: Der Supermarkt-Kassenzettel
In `aufgabe.py` programmierst du zwei Klassen: In `aufgabe.py` programmierst du zwei Klassen:

View file

@ -19,6 +19,7 @@ class Artikel:
# TODO 2: Methode "gesamtpreis(self)" # TODO 2: Methode "gesamtpreis(self)"
# Gibt den Preis für diesen Artikel multipliziert mit der Anzahl zurück. # Gibt den Preis für diesen Artikel multipliziert mit der Anzahl zurück.
# Beispiel: 3 Äpfel à 0.50€ -> 1.50 # Beispiel: 3 Äpfel à 0.50€ -> 1.50
# Tipp: return self.preis * self.anzahl
# ========================================================================== # ==========================================================================
def gesamtpreis(self): def gesamtpreis(self):
# Schreibe hier deinen Code für TODO 2: # Schreibe hier deinen Code für TODO 2:
@ -26,11 +27,10 @@ class Artikel:
# ========================================================================== # ==========================================================================
# TODO 3: Dunder-Methode "__str__(self)" # 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} €" # "{anzahl}x {name} (je {preis:.2f} €) = {gesamtpreis:.2f} €"
# #
# Beispiel: # Beispiel: Bei name="Milch", preis=1.29, anzahl=2:
# Bei name="Milch", preis=1.29, anzahl=2:
# -> "2x Milch (je 1.29 €) = 2.58 €" # -> "2x Milch (je 1.29 €) = 2.58 €"
# ========================================================================== # ==========================================================================
def __str__(self): def __str__(self):
@ -50,7 +50,7 @@ class Warenkorb:
# ========================================================================== # ==========================================================================
# TODO 5: Methode "artikel_hinzufuegen(self, artikel)" # 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): def artikel_hinzufuegen(self, artikel):
# Schreibe hier deinen Code für TODO 5: # Schreibe hier deinen Code für TODO 5:
@ -59,8 +59,13 @@ class Warenkorb:
# ========================================================================== # ==========================================================================
# TODO 6: Methode "gesamtsumme(self)" # TODO 6: Methode "gesamtsumme(self)"
# Berechnet die Summe der Gesamtpreise aller Artikel in self.artikel_liste # Berechnet die Summe der Gesamtpreise aller Artikel in self.artikel_liste
# und gibt diese als float zurück. (Tipp: Schleife oder sum()) # und gibt diese als float zurück.
# Bei leerem Warenkorb soll 0.0 zurückgegeben werden. #
# Tipp mit Schleife:
# total = 0.0
# for item in self.artikel_liste:
# total += item.gesamtpreis()
# return total
# ========================================================================== # ==========================================================================
def gesamtsumme(self): def gesamtsumme(self):
# Schreibe hier deinen Code für TODO 6: # Schreibe hier deinen Code für TODO 6:
@ -76,8 +81,7 @@ class Warenkorb:
# -------------------- # --------------------
# Gesamtsumme: 5.07 € # Gesamtsumme: 5.07 €
# #
# Tipp: Baue eine Liste von Zeilen und verbinde sie mit "\n".join(...) # Tipp: Baue eine Liste von Zeilen und verbinde sie am Ende mit "\n".join(...)
# Die Zeilen für die Artikel erhältst du einfach mit str(artikel)!
# ========================================================================== # ==========================================================================
def bon_text(self): def bon_text(self):
# Schreibe hier deinen Code für TODO 7: # Schreibe hier deinen Code für TODO 7:
@ -86,6 +90,7 @@ class Warenkorb:
# ============================================================================== # ==============================================================================
# Hauptprogramm zum Ausprobieren: # Hauptprogramm zum Ausprobieren:
# (Ausführen mit 'python3 aufgabe.py')
# ============================================================================== # ==============================================================================
if __name__ == "__main__": if __name__ == "__main__":
print("🛒 --- SUPERMARKT EINKAUF --- 🛒") print("🛒 --- SUPERMARKT EINKAUF --- 🛒")
@ -95,11 +100,10 @@ if __name__ == "__main__":
a2 = Artikel("Brot", 2.49, 1) a2 = Artikel("Brot", 2.49, 1)
a3 = Artikel("Schokolade", 0.99, 3) a3 = Artikel("Schokolade", 0.99, 3)
korb.artikel_hinzufuegen(a1) if hasattr(korb, "artikel_liste"):
korb.artikel_hinzufuegen(a2) korb.artikel_hinzufuegen(a1)
korb.artikel_hinzufuegen(a3) korb.artikel_hinzufuegen(a2)
korb.artikel_hinzufuegen(a3)
if korb.artikel_liste:
print(korb.bon_text()) print(korb.bon_text())
else: else:
print("Hinweis: Implementiere die TODOs, um den Kassenbon zu drucken!") print("💡 Hinweis: Implementiere die TODOs, um den Kassenbon zu drucken!")

View file

@ -0,0 +1,155 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kapitel 04: __str__ und Darstellung</title>
<link rel="stylesheet" href="../assets/style.css">
</head>
<body>
<header>
<div class="header-container">
<nav class="nav-links">
<a href="../index.html">🏠 Zur Kursübersicht</a>
<span>&rsaquo;</span>
<a href="../03_methoden_und_verhalten/index.html">Kapitel 03</a>
<span>&rsaquo;</span>
<span style="color: white; font-weight: 600;">Kapitel 04</span>
</nav>
<div class="header-badge">Kapitel 04</div>
<h1>Schöne Ausgaben mit <code>__str__</code> &amp; Objektlisten 🧾</h1>
<p>Lerne, wie du bestimmst, was beim <code>print(objekt)</code> erscheint und wie Objekte andere Objekte in Listen verwalten.</p>
</div>
</header>
<main class="container">
<!-- Theorie Card -->
<section class="card">
<h2>1. Warum sieht <code>print(objekt)</code> standardmäßig so hässlich aus?</h2>
<p>
Wenn du versuchst, ein Objekt ohne Dunder-Methode zu drucken:
</p>
<div class="code-container">
<pre><span class="var">mein_buch</span> = <span class="fn">Buch</span>(<span class="str">"Harry Potter"</span>, <span class="str">"J.K. Rowling"</span>)
<span class="fn">print</span>(<span class="var">mein_buch</span>) <span class="cm"># Ausgabe: &lt;__main__.Buch object at 0x7fa2b0485d90&gt;</span></pre>
</div>
<p>
Python weiß ohne deine Hilfe nicht, wie du dein Objekt darstellen möchtest.
</p>
<div class="box box-tipp">
<div class="box-title">✨ Die Magie von <code>__str__</code>:</div>
Sobald du in deiner Klasse eine Methode <code>def __str__(self):</code> definierst, ruft Python diese automatisch auf, wann immer das Objekt zu Text umgewandelt wird (bei <code>print()</code>, <code>str()</code> oder in f-Strings)!
</div>
<div class="code-container">
<div class="code-header">
<div class="code-dots">
<span class="code-dot dot-red"></span>
<span class="code-dot dot-yellow"></span>
<span class="code-dot dot-green"></span>
</div>
<span>Python</span>
</div>
<pre><span class="kw">class</span> <span class="fn">Buch</span>:
<span class="kw">def</span> <span class="fn">__init__</span>(<span class="var">self</span>, <span class="var">titel</span>, <span class="var">autor</span>):
<span class="var">self</span>.<span class="attr">titel</span> = <span class="var">titel</span>
<span class="var">self</span>.<span class="attr">autor</span> = <span class="var">autor</span>
<span class="kw">def</span> <span class="fn">__str__</span>(<span class="var">self</span>):
<span class="kw">return</span> <span class="str">f"'{self.titel}' von {self.autor}"</span> <span class="cm"># MUSS ein String sein!</span>
<span class="var">mein_buch</span> = <span class="fn">Buch</span>(<span class="str">"Harry Potter"</span>, <span class="str">"J.K. Rowling"</span>)
<span class="fn">print</span>(<span class="var">mein_buch</span>) <span class="cm"># Ausgabe: 'Harry Potter' von J.K. Rowling</span></pre>
</div>
</section>
<!-- Objektlisten Card -->
<section class="card">
<h2>2. Objekte in Listen verwalten</h2>
<p>
Eine Klasse kann als Attribut eine <strong>Liste von anderen Objekten</strong> haben:
</p>
<div class="box box-analogie">
<div class="box-title">🛒 Der Warenkorb:</div>
Ein <code>Warenkorb</code> besitzt eine Liste <code>self.artikel_liste = []</code>. Wenn du einen <code>Artikel</code> hinzufügst, landet das ganze Artikel-Objekt in der Liste!
</div>
</section>
<!-- Aufgaben Card -->
<section class="card">
<h2>3. Deine Aufgaben in <code>aufgabe.py</code></h2>
<ul class="task-list">
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t4_1">
<label for="t4_1" class="task-label">
<strong>TODO 1:</strong> <code>Artikel.__init__(self, name, preis, anzahl=1)</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t4_2">
<label for="t4_2" class="task-label">
<strong>TODO 2:</strong> <code>Artikel.gesamtpreis(self)</code> -> <code>self.preis * self.anzahl</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t4_3">
<label for="t4_3" class="task-label">
<strong>TODO 3:</strong> <code>Artikel.__str__(self)</code> -> z.B. <code>"2x Milch (je 1.29 €) = 2.58 €"</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t4_4">
<label for="t4_4" class="task-label">
<strong>TODO 4-7:</strong> <code>Warenkorb</code> mit <code>artikel_hinzufuegen</code>, <code>gesamtsumme</code> und <code>bon_text</code>.
</label>
</li>
</ul>
<h3>💡 Lösungshinweise (Aufklappen bei Bedarf)</h3>
<details>
<summary>Tipp zu TODO 7 (Kassenzettel zusammensetzen)</summary>
<div class="details-content">
<div class="code-container">
<pre><span class="kw">def</span> <span class="fn">bon_text</span>(<span class="var">self</span>):
<span class="var">zeilen</span> = [<span class="str">"--- KASSENZETTEL ---"</span>]
<span class="kw">for</span> <span class="var">artikel</span> <span class="kw">in</span> <span class="var">self</span>.<span class="attr">artikel_liste</span>:
<span class="var">zeilen</span>.<span class="fn">append</span>(<span class="fn">str</span>(<span class="var">artikel</span>)) <span class="cm"># Nutzt automatisch __str__ des Artikels!</span>
<span class="var">zeilen</span>.<span class="fn">append</span>(<span class="str">"--------------------"</span>)
<span class="var">zeilen</span>.<span class="fn">append</span>(<span class="str">f"Gesamtsumme: {self.gesamtsumme():.2f} €"</span>)
<span class="kw">return</span> <span class="str">"\n"</span>.<span class="fn">join</span>(<span class="var">zeilen</span>)</pre>
</div>
</div>
</details>
</section>
<!-- Testen & Footer Navigation -->
<section class="card">
<h2>4. Lösung überprüfen</h2>
<p>Führe im Terminal im Ordner <code>04_str_und_darstellung</code> folgenden Befehl aus:</p>
<div class="code-container">
<div class="code-header">
<span>Terminal</span>
</div>
<pre><code>python3 test_aufgabe.py</code></pre>
</div>
<div class="footer-nav">
<a href="../03_methoden_und_verhalten/index.html" class="btn btn-secondary">&larr; Zurück zu Kapitel 03</a>
<a href="../05_objekte_kombinieren/index.html" class="btn">Weiter zu Kapitel 05 &rarr;</a>
</div>
</section>
</main>
</body>
</html>

View file

@ -20,7 +20,11 @@ class Song:
# TODO 2: Methode "formatierte_dauer(self)" # TODO 2: Methode "formatierte_dauer(self)"
# Rechnet 'self.dauer_sekunden' in Minuten und Sekunden um und gibt einen # 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"). # 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): def formatierte_dauer(self):
# Schreibe hier deinen Code für TODO 2: # Schreibe hier deinen Code für TODO 2:
@ -28,8 +32,8 @@ class Song:
# ========================================================================== # ==========================================================================
# TODO 3: Dunder-Methode "__str__(self)" # TODO 3: Dunder-Methode "__str__(self)"
# Gibt einen String im folgenden Format zurück: # Gibt einen String im Format zurück:
# '"{titel}" von {kuenstler} ({dauer})' # '"{titel}" von {kuenstler} ({formatierte_dauer})'
# #
# Beispiel: '"Flowers" von Miley Cyrus (03:20)' # Beispiel: '"Flowers" von Miley Cyrus (03:20)'
# ========================================================================== # ==========================================================================
@ -50,7 +54,7 @@ class Playlist:
# ========================================================================== # ==========================================================================
# TODO 5: Methode "song_hinzufuegen(self, song)" # 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): def song_hinzufuegen(self, song):
# Schreibe hier deinen Code für TODO 5: # Schreibe hier deinen Code für TODO 5:
@ -58,7 +62,7 @@ class Playlist:
# ========================================================================== # ==========================================================================
# TODO 6: Methode "anzahl_songs(self)" # 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): def anzahl_songs(self):
# Schreibe hier deinen Code für TODO 6: # Schreibe hier deinen Code für TODO 6:
@ -68,6 +72,12 @@ class Playlist:
# TODO 7: Methode "gesamtdauer_sekunden(self)" # TODO 7: Methode "gesamtdauer_sekunden(self)"
# Gibt die Summe der Sekunden aller Songs in der Playlist zurück. # Gibt die Summe der Sekunden aller Songs in der Playlist zurück.
# Bei leerer Playlist: 0 # Bei leerer Playlist: 0
#
# Tipp:
# total = 0
# for s in self.songs:
# total += s.dauer_sekunden
# return total
# ========================================================================== # ==========================================================================
def gesamtdauer_sekunden(self): def gesamtdauer_sekunden(self):
# Schreibe hier deinen Code für TODO 7: # Schreibe hier deinen Code für TODO 7:
@ -96,6 +106,7 @@ class Playlist:
# ============================================================================== # ==============================================================================
# Hauptprogramm zum Ausprobieren: # Hauptprogramm zum Ausprobieren:
# (Ausführen mit 'python3 aufgabe.py')
# ============================================================================== # ==============================================================================
if __name__ == "__main__": if __name__ == "__main__":
print("🎧 --- MEINE PLAYLIST --- 🎧") print("🎧 --- MEINE PLAYLIST --- 🎧")
@ -106,18 +117,21 @@ if __name__ == "__main__":
s3 = Song("Anti-Hero", "Taylor Swift", 201) s3 = Song("Anti-Hero", "Taylor Swift", 201)
s4 = Song("As It Was", "Harry Styles", 167) s4 = Song("As It Was", "Harry Styles", 167)
meine_hits.song_hinzufuegen(s1) if hasattr(meine_hits, "songs"):
meine_hits.song_hinzufuegen(s2) meine_hits.song_hinzufuegen(s1)
meine_hits.song_hinzufuegen(s3) meine_hits.song_hinzufuegen(s2)
meine_hits.song_hinzufuegen(s4) meine_hits.song_hinzufuegen(s3)
meine_hits.song_hinzufuegen(s4)
print(f"Playlist: {meine_hits.name} ({meine_hits.anzahl_songs()} Songs)") print(f"Playlist: {meine_hits.name} ({meine_hits.anzahl_songs()} Songs)")
print(f"Gesamtlaufzeit: {meine_hits.gesamtdauer_sekunden()} Sekunden") print(f"Gesamtlaufzeit: {meine_hits.gesamtdauer_sekunden()} Sekunden")
laengster = meine_hits.laengster_song() laengster = meine_hits.laengster_song()
if laengster: if laengster:
print(f"Längster Song: {laengster}") print(f"Längster Song: {laengster}")
print("\nSongs von Taylor Swift:") print("\nSongs von Taylor Swift:")
for s in meine_hits.finde_songs_von("taylor swift"): for s in meine_hits.finde_songs_von("taylor swift"):
print(f"- {s}") print(f"- {s}")
else:
print("💡 Hinweis: Implementiere die TODOs, um die Playlist zu testen!")

View file

@ -0,0 +1,150 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kapitel 05: Komposition und Playlists</title>
<link rel="stylesheet" href="../assets/style.css">
</head>
<body>
<header>
<div class="header-container">
<nav class="nav-links">
<a href="../index.html">🏠 Zur Kursübersicht</a>
<span>&rsaquo;</span>
<a href="../04_str_und_darstellung/index.html">Kapitel 04</a>
<span>&rsaquo;</span>
<span style="color: white; font-weight: 600;">Kapitel 05</span>
</nav>
<div class="header-badge">Kapitel 05</div>
<h1>Komposition: Objekte kombinieren &amp; filtern 🎵</h1>
<p>Baue einen eigenen Spotify-Playlist-Manager und lerne, wie Objekte andere Objekte suchen, berechnen und filtern.</p>
</div>
</header>
<main class="container">
<!-- Theorie Card -->
<section class="card">
<h2>1. Was ist Komposition?</h2>
<p>
In großen Anwendungen (wie Spotify, Netflix oder Videospielen) arbeiten viele Klassen zusammen. Wenn eine Klasse Instanzen einer anderen Klasse beinhaltet, nennt man das <strong>Komposition</strong> (oder *"hat-ein"-Beziehung*):
</p>
<ul style="margin-left: 20px; margin-bottom: 15px;">
<li>Eine <strong>Playlist</strong> <em>hat</em> viele <strong>Songs</strong>.</li>
<li>Ein <strong>Kino</strong> <em>hat</em> viele <strong>Säle</strong> und <strong>Filme</strong>.</li>
</ul>
<div class="box box-analogie">
<div class="box-title">🎧 Die Spotify-Analogie:</div>
Ein <code>Song</code> speichert seinen Titel, den Künstler und die Länge in Sekunden. Die <code>Playlist</code> kümmert sich um die Gesamtübersicht: Wie viele Songs gibt es? Wie lange dauert die Playlist insgesamt? Gibt es Lieder von Taylor Swift?
</div>
</section>
<!-- Filter & Formatierung Card -->
<section class="card">
<h2>2. Nützliche Python-Kniffe</h2>
<h3>⏱️ Sekunden in "MM:SS" umrechnen</h3>
<p>
Mit Ganzzahldivision <code>//</code> und Modulo <code>%</code> rechnen wir Sekunden in Minuten um:
</p>
<div class="code-container">
<div class="code-header">
<span>Python</span>
</div>
<pre><span class="var">gesamt_sekunden</span> = <span class="num">215</span>
<span class="var">minuten</span> = <span class="var">gesamt_sekunden</span> // <span class="num">60</span> <span class="cm"># 3 Minuten</span>
<span class="var">sekunden</span> = <span class="var">gesamt_sekunden</span> % <span class="num">60</span> <span class="cm"># 35 Restsekunden</span>
<span class="cm"># :02d sorgt für zweistellige Zahlen mit führender Null (z.B. 03:05 statt 3:5):</span>
<span class="var">zeit_text</span> = <span class="str">f"{minuten:02d}:{sekunden:02d}"</span> <span class="cm"># "03:35"</span></pre>
</div>
<h3>🔍 Groß- und Kleinschreibung beim Suchen ignorieren</h3>
<p>
Damit <code>"Taylor Swift"</code> auch gefunden wird, wenn jemand <code>"taylor swift"</code> eingibt, nutzen wir <code>.lower()</code>:
</p>
<div class="code-container">
<pre><span class="kw">if</span> <span class="var">song</span>.<span class="attr">kuenstler</span>.<span class="fn">lower</span>() == <span class="var">gesuchter_kuenstler</span>.<span class="fn">lower</span>():
<span class="var">treffer</span>.<span class="fn">append</span>(<span class="var">song</span>)</pre>
</div>
</section>
<!-- Aufgaben Card -->
<section class="card">
<h2>3. Deine Aufgaben in <code>aufgabe.py</code></h2>
<ul class="task-list">
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t5_1">
<label for="t5_1" class="task-label">
<strong>Klasse Song:</strong> <code>__init__(titel, kuenstler, dauer_sekunden)</code>, <code>formatierte_dauer()</code> ("MM:SS") und <code>__str__()</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t5_2">
<label for="t5_2" class="task-label">
<strong>Klasse Playlist:</strong> <code>__init__(name)</code> mit <code>self.songs = []</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t5_3">
<label for="t5_3" class="task-label">
<strong>Playlist Methoden:</strong> <code>song_hinzufuegen</code>, <code>anzahl_songs</code>, <code>gesamtdauer_sekunden</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t5_4">
<label for="t5_4" class="task-label">
<strong>Playlist Suchen &amp; Finden:</strong> <code>finde_songs_von(kuenstler)</code> und <code>laengster_song()</code>.
</label>
</li>
</ul>
<h3>💡 Lösungshinweise (Aufklappen bei Bedarf)</h3>
<details>
<summary>Tipp zum längsten Song finden</summary>
<div class="details-content">
<div class="code-container">
<pre><span class="kw">def</span> <span class="fn">laengster_song</span>(<span class="var">self</span>):
<span class="kw">if</span> <span class="kw">not</span> <span class="var">self</span>.<span class="attr">songs</span>:
<span class="kw">return</span> <span class="kw">None</span>
<span class="var">bester_song</span> = <span class="var">self</span>.<span class="attr">songs</span>[<span class="num">0</span>]
<span class="kw">for</span> <span class="var">s</span> <span class="kw">in</span> <span class="var">self</span>.<span class="attr">songs</span>:
<span class="kw">if</span> <span class="var">s</span>.<span class="attr">dauer_sekunden</span> > <span class="var">bester_song</span>.<span class="attr">dauer_sekunden</span>:
<span class="var">bester_song</span> = <span class="var">s</span>
<span class="kw">return</span> <span class="var">bester_song</span></pre>
</div>
</div>
</details>
</section>
<!-- Testen & Footer Navigation -->
<section class="card">
<h2>4. Lösung überprüfen</h2>
<p>Führe im Terminal im Ordner <code>05_objekte_kombinieren</code> folgenden Befehl aus:</p>
<div class="code-container">
<div class="code-header">
<span>Terminal</span>
</div>
<pre><code>python3 test_aufgabe.py</code></pre>
</div>
<div class="footer-nav">
<a href="../04_str_und_darstellung/index.html" class="btn btn-secondary">&larr; Zurück zu Kapitel 04</a>
<a href="../06_abschlussprojekt_tamagotchi/index.html" class="btn">Weiter zum Abschlussprojekt &rarr;</a>
</div>
</section>
</main>
</body>
</html>

View file

@ -23,7 +23,7 @@ Du kannst mit ihm interagieren:
- 🍎 **Füttern** (`fuettern`): Senkt den Hunger, macht etwas müde und etwas glücklicher. - 🍎 **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! - ⚽ **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. - 💤 **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...
--- ---

View file

@ -9,7 +9,7 @@ class Tamagotchi:
# ========================================================================== # ==========================================================================
# TODO 1: Konstruktor __init__(self, name, tierart="Drache") # TODO 1: Konstruktor __init__(self, name, tierart="Drache")
# #
# Initialisiere die folgenden Attribute: # Initialisiere die folgenden Attribute an self:
# - self.name = name # - self.name = name
# - self.tierart = tierart # - self.tierart = tierart
# - self.hunger = 50 (0 = pappsatt, 100 = verhungert) # - self.hunger = 50 (0 = pappsatt, 100 = verhungert)
@ -22,10 +22,9 @@ class Tamagotchi:
# Schreibe hier deinen Code für TODO 1: # Schreibe hier deinen Code für TODO 1:
pass pass
# Hilfsmethode (optional, aber sehr nützlich): # Hilfsmethode: Begrenzt Werte immer zwischen min_wert (0) und max_wert (100)
# Hält einen Wert zwischen minimal 0 und maximal 100. def _begrenze(self, wert, min_wert=0, max_wert=100):
def _begrenze(self, wert): return max(min_wert, min(max_wert, wert))
return max(0, min(100, wert))
# ========================================================================== # ==========================================================================
# TODO 2: Methode "fuettern(self, menge=20)" # TODO 2: Methode "fuettern(self, menge=20)"
@ -34,9 +33,9 @@ class Tamagotchi:
# 1. Wenn not self.ist_lebendig: # 1. Wenn not self.ist_lebendig:
# - Gib zurück: f"{self.name} reagiert nicht mehr..." # - Gib zurück: f"{self.name} reagiert nicht mehr..."
# 2. Wenn lebendig: # 2. Wenn lebendig:
# - Verringere 'self.hunger' um 'menge' (darf nicht unter 0 fallen). # - Verringere self.hunger um menge (mit _begrenze, darf nicht unter 0 fallen).
# - Erhöhe 'self.glueck' um 5 (darf nicht über 100 steigen). # - Erhöhe self.glueck um 5 (max 100).
# - Erhöhe 'self.muedigkeit' um 5 (darf nicht über 100 steigen). # - Erhöhe self.muedigkeit um 5 (max 100).
# - Gib zurück: f"{self.name} mampft genüsslich! (Hunger: {self.hunger}/100)" # - Gib zurück: f"{self.name} mampft genüsslich! (Hunger: {self.hunger}/100)"
# ========================================================================== # ==========================================================================
def fuettern(self, menge=20): def fuettern(self, menge=20):
@ -49,13 +48,13 @@ class Tamagotchi:
# Regeln: # Regeln:
# 1. Wenn not self.ist_lebendig: # 1. Wenn not self.ist_lebendig:
# - Gib zurück: f"{self.name} reagiert nicht mehr..." # - 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: # - 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..." # f"{self.name} ist zu müde zum Spielen und gähnt nur..."
# 3. Wenn fit genug: # 3. Wenn fit genug:
# - Erhöhe 'self.glueck' um 'spass' (max 100). # - Erhöhe self.glueck um spass (max 100).
# - Erhöhe 'self.hunger' um 15 (max 100). # - Erhöhe self.hunger um 15 (max 100).
# - Erhöhe 'self.muedigkeit' um 20 (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)" # - Gib zurück: f"{self.name} hatte riesigen Spaß beim Spielen! (Glück: {self.glueck}/100)"
# ========================================================================== # ==========================================================================
def spielen(self, spass=25): def spielen(self, spass=25):
@ -69,9 +68,9 @@ class Tamagotchi:
# 1. Wenn not self.ist_lebendig: # 1. Wenn not self.ist_lebendig:
# - Gib zurück: f"{self.name} reagiert nicht mehr..." # - Gib zurück: f"{self.name} reagiert nicht mehr..."
# 2. Wenn lebendig: # 2. Wenn lebendig:
# - Setze 'self.muedigkeit' auf 0. # - Setze self.muedigkeit auf 0.
# - Erhöhe 'self.hunger' um 15 (max 100). # - Erhöhe self.hunger um 15 (max 100).
# - Erhöhe 'self.alter_tage' um 1. # - Erhöhe self.alter_tage um 1.
# - Gib zurück: f"{self.name} hat tief geschlafen und ist ausgeruht! Ein neuer Tag bricht an." # - Gib zurück: f"{self.name} hat tief geschlafen und ist ausgeruht! Ein neuer Tag bricht an."
# ========================================================================== # ==========================================================================
def schlafen(self): def schlafen(self):
@ -86,9 +85,9 @@ class Tamagotchi:
# 1. Wenn not self.ist_lebendig: # 1. Wenn not self.ist_lebendig:
# - Mach nichts, gib False zurück. # - Mach nichts, gib False zurück.
# 2. Wenn lebendig: # 2. Wenn lebendig:
# - Erhöhe 'self.hunger' um 10 (max 100). # - Erhöhe self.hunger um 10 (max 100).
# - Erhöhe 'self.muedigkeit' um 10 (max 100). # - Erhöhe self.muedigkeit um 10 (max 100).
# - Verringere 'self.glueck' um 10 (min 0). # - Verringere self.glueck um 10 (min 0).
# - Prüfe, ob das Haustier stirbt: # - Prüfe, ob das Haustier stirbt:
# Wenn self.hunger >= 100 oder self.glueck <= 0: # Wenn self.hunger >= 100 oder self.glueck <= 0:
# self.ist_lebendig = False # self.ist_lebendig = False
@ -118,12 +117,16 @@ class Tamagotchi:
# ============================================================================== # ==============================================================================
# Schneller Testlauf: # Schneller Testlauf:
# (Ausführen mit 'python3 aufgabe.py' oder starte 'python3 tamagotchi_spiel.py')
# ============================================================================== # ==============================================================================
if __name__ == "__main__": if __name__ == "__main__":
pet = Tamagotchi("Yoshi", "Dino") pet = Tamagotchi("Yoshi", "Dino")
print(pet) if hasattr(pet, "name") and pet.name:
print() print(pet)
print(pet.fuettern()) print()
print(pet.spielen()) print(pet.fuettern())
print(pet.schlafen()) print(pet.spielen())
print(pet) print(pet.schlafen())
print(pet)
else:
print("💡 Hinweis: Implementiere erst die TODOs, um Yoshi zum Leben zu erwecken!")

View file

@ -0,0 +1,133 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kapitel 06: Tamagotchi Abschlussprojekt</title>
<link rel="stylesheet" href="../assets/style.css">
</head>
<body>
<header>
<div class="header-container">
<nav class="nav-links">
<a href="../index.html">🏠 Zur Kursübersicht</a>
<span>&rsaquo;</span>
<a href="../05_objekte_kombinieren/index.html">Kapitel 05</a>
<span>&rsaquo;</span>
<span style="color: white; font-weight: 600;">Kapitel 06</span>
</nav>
<div class="header-badge">Abschlussprojekt</div>
<h1>Dein eigenes Tamagotchi (Virtuelles Haustier) 🥚🐣</h1>
<p>Kombiniere alle Konzepte in einem interaktiven, voll funktionsfähigen Konsolenspiel!</p>
</div>
</header>
<main class="container">
<!-- Übersicht Card -->
<section class="card">
<h2>1. Das große Finale! 🏆</h2>
<p>
Du hast alle Grundlagen gelernt: Klassen definieren, <code>__init__</code> &amp; <code>self</code> nutzen, Methoden schreiben, <code>__str__</code> gestalten und Objekte verwalten.
</p>
<p>
Jetzt bauen wir ein komplettes, lebendiges <strong>Tamagotchi</strong>, das du im Terminal füttern, schlafen legen und mit dem du spielen kannst!
</p>
<div class="box box-analogie">
<div class="box-title">🎮 Wie das Spiel funktioniert:</div>
<ul style="margin-left: 20px;">
<li>🍖 <strong>Hunger</strong> (0 bis 100): Steigt mit der Zeit, sinkt beim Füttern. Bei 100 stirbt das Haustier!</li>
<li>💤 <strong>Müdigkeit</strong> (0 bis 100): Steigt bei Aktionen. Bei über 80 weigert es sich zu spielen. Schlafen setzt Müdigkeit auf 0.</li>
<li>💖 <strong>Glück</strong> (0 bis 100): Steigt beim Spielen, sinkt mit der Zeit. Bei 0 stirbt das Haustier vor Einsamkeit.</li>
<li>📅 <strong>Alter</strong>: Bei jedem Schlafen vergeht ein neuer Tag (<code>alter_tage += 1</code>).</li>
</ul>
</div>
</section>
<!-- Aufgaben Card -->
<section class="card">
<h2>2. Deine Aufgaben in <code>aufgabe.py</code></h2>
<ul class="task-list">
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t6_1">
<label for="t6_1" class="task-label">
<strong>TODO 1:</strong> Konstruktor <code>__init__(self, name, tierart="Drache")</code>: Startwerte <code>hunger=50</code>, <code>muedigkeit=20</code>, <code>glueck=80</code>, <code>alter_tage=0</code>, <code>ist_lebendig=True</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t6_2">
<label for="t6_2" class="task-label">
<strong>TODO 2:</strong> Methode <code>fuettern(self, menge=20)</code>: Senkt Hunger, erhöht Glück &amp; Müdigkeit um 5.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t6_3">
<label for="t6_3" class="task-label">
<strong>TODO 3:</strong> Methode <code>spielen(self, spass=25)</code>: Steigert Glück, erhöht Hunger (15) &amp; Müdigkeit (20). Wenn <code>muedigkeit > 80</code> -> ablehnen!
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t6_4">
<label for="t6_4" class="task-label">
<strong>TODO 4:</strong> Methode <code>schlafen(self)</code>: Müdigkeit auf 0, Hunger +15, <code>alter_tage += 1</code>.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t6_5">
<label for="t6_5" class="task-label">
<strong>TODO 5:</strong> Methode <code>zeit_vergeht(self)</code>: Hunger +10, Müdigkeit +10, Glück -10. Prüfen, ob Hunger &gt;= 100 oder Glück &lt;= 0.
</label>
</li>
<li class="task-item">
<input type="checkbox" class="task-checkbox" id="t6_6">
<label for="t6_6" class="task-label">
<strong>TODO 6:</strong> Dunder-Methode <code>__str__(self)</code>: Statusanzeige mit Name, Tierart, Alter und Werten.
</label>
</li>
</ul>
<h3>💡 Lösungshinweise (Aufklappen bei Bedarf)</h3>
<details>
<summary>Tipp zur Werte-Begrenzung (Clamping)</summary>
<div class="details-content">
<p>Damit Werte nicht negativ werden oder 100 überschreiten, kannst du die Hilfsfunktion <code>max(0, min(100, wert))</code> nutzen:</p>
<div class="code-container">
<pre><span class="var">self</span>.<span class="attr">hunger</span> = <span class="fn">max</span>(<span class="num">0</span>, <span class="fn">min</span>(<span class="num">100</span>, <span class="var">self</span>.<span class="attr">hunger</span> - <span class="var">menge</span>))</pre>
</div>
</div>
</details>
</section>
<!-- Spiel starten Card -->
<section class="card">
<h2>3. Das interaktive Spiel starten! 🕹️</h2>
<p>Sobald deine Tests grün sind, kannst du das Spiel live spielen:</p>
<div class="code-container">
<div class="code-header">
<span>Terminal</span>
</div>
<pre><code>python3 tamagotchi_spiel.py</code></pre>
</div>
<div class="box box-success">
<div class="box-title">🎉 Geschafft!</div>
Du hast damit erfolgreich gelernt, wie man objektorientiert in Python programmiert!
</div>
<div class="footer-nav">
<a href="../05_objekte_kombinieren/index.html" class="btn btn-secondary">&larr; Zurück zu Kapitel 05</a>
<a href="../index.html" class="btn">Zurück zur Hauptübersicht 🏠</a>
</div>
</section>
</main>
</body>
</html>

449
assets/style.css Normal file
View file

@ -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; }
}

166
index.html Normal file
View file

@ -0,0 +1,166 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Python OOP Lernportal: Klassen & Objekte</title>
<link rel="stylesheet" href="assets/style.css">
</head>
<body>
<header>
<div class="header-container">
<div class="header-badge">🐍 Python Nachhilfe & Selbstlern-Kurs</div>
<h1>Objektorientierte Programmierung (OOP)</h1>
<p>Lerne Schritt für Schritt, wie du eigene Klassen baust, Methoden nutzt und komplexe Programme strukturierst.</p>
</div>
</header>
<main class="container">
<!-- Willkommen Card -->
<section class="card">
<h2>👋 Willkommen zu deiner Übungsreihe!</h2>
<p>
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: <strong>Klassen & Objekte</strong>!
</p>
<p>
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.
</p>
<div class="box box-analogie">
<div class="box-title">🏗️ Die Grundidee in einem Satz:</div>
Eine <strong>Klasse</strong> ist wie ein Bauplan (oder eine Plätzchen-Ausstechform). Ein <strong>Objekt</strong> ist das fertige Haus (oder das gebackene Plätzchen), das nach diesem Bauplan entstanden ist!
</div>
</section>
<!-- Roadmap Grid -->
<section>
<h2 style="margin: 30px 0 15px 0; color: var(--text-main); font-size: 1.5rem;">🗺️ Dein Lernpfad (Kapitel 01 bis 06)</h2>
<div class="roadmap-grid">
<!-- Kapitel 1 -->
<a href="01_einstieg_klassen/index.html" class="roadmap-card">
<div>
<span class="roadmap-number">Kapitel 01</span>
<div class="roadmap-title">🐶 Die erste Klasse</div>
<div class="roadmap-desc">Was ist eine Klasse? Bauplan vs. Objekt, Instanziierung und erste Attribute am Haustier-Beispiel.</div>
</div>
<div class="roadmap-footer">Kapitel öffnen &rarr;</div>
</a>
<!-- Kapitel 2 -->
<a href="02_init_und_self/index.html" class="roadmap-card">
<div>
<span class="roadmap-number">Kapitel 02</span>
<div class="roadmap-title">💳 Konstruktor & self</div>
<div class="roadmap-desc">Wie <code>__init__</code> Objekte startklar macht und was das Wörtchen <code>self</code> wirklich bedeutet (Bankkonto-App).</div>
</div>
<div class="roadmap-footer">Kapitel öffnen &rarr;</div>
</a>
<!-- Kapitel 3 -->
<a href="03_methoden_und_verhalten/index.html" class="roadmap-card">
<div>
<span class="roadmap-number">Kapitel 03</span>
<div class="roadmap-title">⚔️ Methoden & Verhalten</div>
<div class="roadmap-desc">Objekte führen Aktionen aus! Interaktion zwischen Helden und Monstern in einem RPG-Kampfsimulator.</div>
</div>
<div class="roadmap-footer">Kapitel öffnen &rarr;</div>
</a>
<!-- Kapitel 4 -->
<a href="04_str_und_darstellung/index.html" class="roadmap-card">
<div>
<span class="roadmap-number">Kapitel 04</span>
<div class="roadmap-title">🧾 Schöne Textausgabe</div>
<div class="roadmap-desc">Wie man <code>print(objekt)</code> mit <code>__str__</code> lesbar macht und Objekte im Warenkorb sammelt.</div>
</div>
<div class="roadmap-footer">Kapitel öffnen &rarr;</div>
</a>
<!-- Kapitel 5 -->
<a href="05_objekte_kombinieren/index.html" class="roadmap-card">
<div>
<span class="roadmap-number">Kapitel 05</span>
<div class="roadmap-title">🎵 Komposition & Playlists</div>
<div class="roadmap-desc">Objekte verwalten andere Objekte: Song-Suche, Längenberechnung und Filter in Spotify-Playlists.</div>
</div>
<div class="roadmap-footer">Kapitel öffnen &rarr;</div>
</a>
<!-- Kapitel 6 -->
<a href="06_abschlussprojekt_tamagotchi/index.html" class="roadmap-card">
<div>
<span class="roadmap-number">Kapitel 06</span>
<div class="roadmap-title">🥚 Tamagotchi Mini-Projekt</div>
<div class="roadmap-desc">Kombiniere alles in einem voll spielbaren, interaktiven virtuellen Haustier für dein Terminal!</div>
</div>
<div class="roadmap-footer">Kapitel öffnen &rarr;</div>
</a>
</div>
</section>
<!-- Wie lerne ich Card -->
<section class="card">
<h2>🚀 Wie du vorgehst (Schritt-für-Schritt)</h2>
<ol style="margin-left: 20px; margin-bottom: 15px;">
<li style="margin-bottom: 8px;">Wähle das nächste offene Kapitel aus der Liste oben.</li>
<li style="margin-bottom: 8px;">Lies die Erklärungen und schau dir die Codebeispiele und „Stolperfallen“ an.</li>
<li style="margin-bottom: 8px;">Öffne die <code>aufgabe.py</code> im entsprechenden Kapitelordner und löse die <code>TODO</code>s.</li>
<li style="margin-bottom: 8px;">
Führe im Terminal den automatischen Test aus:
<div class="code-container" style="margin: 10px 0;">
<div class="code-header">
<span>Terminal</span>
</div>
<pre><code>python3 test_aufgabe.py</code></pre>
</div>
</li>
<li>Wenn alle Tests mit <strong>OK</strong> durchlaufen, hast du das Kapitel erfolgreich gemeistert! 🎉</li>
</ol>
<div class="box box-tipp">
<div class="box-title">💡 Gesamten Fortschritt auf einen Blick prüfen:</div>
Du kannst jederzeit im Hauptordner folgenden Befehl ausführen, um zu sehen, wie viele Kapitel du schon gelöst hast:
<div class="code-container" style="margin: 8px 0 0 0;">
<pre><code>python3 test_all.py</code></pre>
</div>
</div>
</section>
<!-- FAQ & Troubleshooting Card -->
<section class="card">
<h2>🩺 Erste Hilfe & Häufige Fragen</h2>
<details>
<summary>❌ Fehlermeldung: <code>TypeError: ... takes 0 positional arguments but 1 was given</code></summary>
<div class="details-content">
<p><strong>Ursache:</strong> Du hast bei einer Methode in deiner Klasse das <code>self</code> im Methodenkopf vergessen!</p>
<p><strong>Lösung:</strong> Schreibe <code>def meine_methode(self):</code> statt <code>def meine_methode():</code>. Python übergibt bei Methodenaufrufen immer automatisch das Objekt selbst als erstes Argument.</p>
</div>
</details>
<details>
<summary>❌ Fehlermeldung: <code>AttributeError: 'NoneType' object has no attribute '...'</code></summary>
<div class="details-content">
<p><strong>Ursache:</strong> Eine Funktion oder Methode hat nichts zurückgegeben (also <code>None</code>), und du versuchst, auf ein Attribut davon zuzugreifen.</p>
<p><strong>Lösung:</strong> Prüfe, ob du am Ende der Funktion das <code>return objekt</code> vergessen hast!</p>
</div>
</details>
<details>
<summary>❌ Der Test schlägt fehl, obwohl ich die richtige Ausgabe in der Konsole sehe?</summary>
<div class="details-content">
<p><strong>Ursache:</strong> Du hast vermutlich <code>print(...)</code> verwendet, anstatt den Wert mit <code>return ...</code> zurückzugeben.</p>
<p><strong>Lösung:</strong> Die automatischen Tests prüfen den Rückgabewert (<code>return</code>) deiner Funktionen/Methoden, nicht das, was auf dem Bildschirm ausgegeben wird.</p>
</div>
</details>
</section>
</main>
</body>
</html>