diff --git a/01_einstieg_klassen/README.md b/01_einstieg_klassen/README.md index 8178b43..3c132f7 100644 --- a/01_einstieg_klassen/README.md +++ b/01_einstieg_klassen/README.md @@ -37,48 +37,47 @@ Beim Dictionary kann man sich schnell bei Schlüsseln vertippen. Eine Klasse gib --- -## 💻 Wie sieht eine Klasse in Python aus? +## 🌟 Vollständiges Praxisbeispiel: Der Fahrrad-Verleih -Eine einfache Klasse definierst du mit dem Schlüsselwort `class`: +Hier siehst du ein komplettes, funktionierendes Beispiel: ```python -class Auto: - pass # 'pass' bedeutet: "Hier passiert erst mal nichts weiter" +# 1. Bauplan definieren: +class Fahrrad: + pass + +# 2. Hilfsfunktion, die mit Fahrrad-Objekten arbeitet: +def fahrrad_info(bike): + return f"{bike.marke} ({bike.farbe}) - {bike.gaenge} Gänge - {bike.preis_pro_tag}€/Tag" + +# 3. Zwei konkrete Fahrräder (Objekte) erstellen: +mountainbike = Fahrrad() +mountainbike.marke = "Cube" +mountainbike.farbe = "Schwarz/Grün" +mountainbike.gaenge = 21 +mountainbike.preis_pro_tag = 25.0 + +citybike = Fahrrad() +citybike.marke = "Gazelle" +citybike.farbe = "Himmelblau" +citybike.gaenge = 7 +citybike.preis_pro_tag = 15.0 + +# 4. Ausgabe: +print("Rad 1:", fahrrad_info(mountainbike)) +print("Rad 2:", fahrrad_info(citybike)) ``` -Aus dieser Klasse kannst du nun ein Objekt erzeugen (**Instanziierung**): - -```python -# Objekt erstellen: -mein_auto = Auto() - -# Eigenschaften (Attribute) zuweisen mit dem Punkt (.): -mein_auto.farbe = "Rot" -mein_auto.marke = "VW" -mein_auto.ps = 110 - -print(mein_auto.farbe) # Gibt "Rot" aus -``` - -Du kannst ganz einfach ein zweites Auto bauen: -```python -dein_auto = Auto() -dein_auto.farbe = "Blau" -dein_auto.marke = "BMW" -dein_auto.ps = 180 -``` -`mein_auto` und `dein_auto` sind zwei völlig eigenständige Objekte, die aus demselben Bauplan `Auto` entstanden sind! - --- ## ⚠️ Häufige Stolperfallen für Einsteiger -1. **Groß- und Kleinschreibung:** - - Klassen werden per Konvention in **CamelCase** mit großem Anfangsbuchstaben geschrieben: `class Haustier:`, `class BankKonto:`. - - Variablen und Funktionen schreibt man klein mit Unterstrichen: `mein_haustier = Haustier()`. -2. **Klammern beim Erstellen nicht vergessen:** +1. **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! +2. **Groß- und Kleinschreibung:** + - Klassen werden per Konvention in **CamelCase** mit großem Anfangsbuchstaben geschrieben: `class Haustier:`, `class Fahrrad:`. + - Variablen und Funktionen schreibt man klein: `mein_tier`, `fahrrad_info()`. 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. @@ -86,7 +85,7 @@ dein_auto.ps = 180 ## 🎯 Deine Aufgaben in `aufgabe.py` -Öffne jetzt die Datei `aufgabe.py` und bearbeite die nummerierten TODOs: +Öffne 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()`. @@ -94,7 +93,6 @@ dein_auto.ps = 180 5. **TODO 5:** Schreibe die Funktion `aelteres_tier(tier1, tier2)`. ### Testen: -Führe deine Tests aus mit: ```bash python3 test_aufgabe.py ``` diff --git a/01_einstieg_klassen/index.html b/01_einstieg_klassen/index.html index 8426656..5aa6bae 100644 --- a/01_einstieg_klassen/index.html +++ b/01_einstieg_klassen/index.html @@ -17,54 +17,35 @@
Kapitel 01

Die erste Klasse: Bauplan vs. Objekt 🐶

-

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

+

Verstehe den Unterschied zwischen einer Klasse und einer Instanz anhand vollständiger Praxisbeispiele.

- +
-

1. Die wichtigste Idee: Bauplan vs. Haus

+

1. Die Grundidee: Bauplan vs. Echtes Objekt

- In Python ist alles, was du bisher kennengelernt hast (Zahlen, Strings, Listen, Dictionaries), im Grunde ein Objekt. Doch jetzt lernst du, wie du deine ganz eigenen Datentypen erfindest! + In Python ist fast alles ein Objekt (Zahlen, Texte, Listen). Mit Klassen lernst du jetzt das mächtigste Werkzeug der Programmierung kennen: Deine eigenen Baupläne zu entwerfen!

🏗️ Die Plätzchenform-Analogie:
-

Stell dir das Backen vor:

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

    Stell dir das Backen in der Küche vor:

    +
      +
    • Die Klasse (class) ist die Ausstechform. Sie legt die Form fest (Kopf, Arme, Beine). Die Form selbst kann man nicht essen.
    • +
    • Das Objekt (Instanz) ist das echte Plätzchen, das du aus dem Teig ausstichst. Ein Plätzchen bekommt Schokoglasur, ein anderes bunte Streusel. Beide stammen aus derselben Form, sind aber zwei völlig eigenständige Plätzchen!
- -

Warum nicht einfach Dictionaries?

-

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

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

Mit einer Klasse definierst du eine klare, wiederverwendbare Struktur.

- +
-

2. Wie definiert und nutzt man eine Klasse?

- -

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

+

2. Warum machen Klassen deinen Code besser?

+ +

Schauen wir uns an, wie man Daten bisher mit Dictionaries gespeichert hat – und warum eine Klasse viel übersichtlicher und sicherer ist:

@@ -73,35 +54,151 @@
- Python + ❌ Vorher: Mit Dictionaries (Fehleranfällig!)
-
# 1. Bauplan definieren:
-class Auto:
-    pass  # pass bedeutet: Hier steht erst mal kein weiterer Code
+        
# Hund 1 mit Dictionary
+hund1 = {"name": "Bello", "rasse": "Labrador", "alter": 3}
 
-# 2. Ein Objekt erzeugen (Instanziierung):
-mein_auto = Auto()
+# Hund 2: Ups! Hier haben wir uns beim Schlüssel vertippt ("tier_rasse" statt "rasse")!
+hund2 = {"name": "Luna", "tier_rasse": "Pudel", "alter": 2}
 
-# 3. Eigenschaften (Attribute) mit dem Punkt (.) setzen:
-mein_auto.farbe = "Rot"
-mein_auto.marke = "VW"
-mein_auto.ps = 110
+# Wenn wir jetzt die Rasse ausgeben wollen:
+print(hund1["rasse"])  # Funktioniert -> "Labrador"
+print(hund2["rasse"])  # 💥 KeyError: 'rasse' existiert nicht! Programm stürzt ab!
+ -# 4. Werte wieder abfragen: -print(mein_auto.farbe) # Gibt "Rot" aus
+

Jetzt das Ganze mit einer Klasse:

+ +
+
+
+ + + +
+ ✅ Nachher: Mit einer Klasse (Sauber & Einheitlich) +
+
# 1. Wir definieren den gemeinsamen Bauplan:
+class Hund:
+    pass
+
+# 2. Wir erstellen zwei konkrete Hunde (Instanzen):
+bello = Hund()
+bello.name = "Bello"
+bello.rasse = "Labrador"
+bello.alter = 3
+
+luna = Hund()
+luna.name = "Luna"
+luna.rasse = "Pudel"
+luna.alter = 2
+
+# 3. Jetzt greifen wir mit der Punkt-Notation (.) auf die Eigenschaften zu:
+print(bello.name, "ist ein", bello.rasse)  # Ausgabe: Bello ist ein Labrador
+print(luna.name, "ist ein", luna.rasse)    # Ausgabe: Luna ist ein Pudel
+
+
+ + +
+

3. 🌟 Vollständiges Praxisbeispiel: Der Fahrrad-Verleih

+ +

Hier siehst du ein vollständiges, funktionierendes Python-Skript von Anfang bis Ende. Schau dir genau an, wie Objekte erstellt, Eigenschaften zugewiesen und in Funktionen verwendet werden:

+ +
+
+
+ + + +
+ Python (Komplettes Beispiel: fahrrad_verleih.py) +
+
# ==============================================================================
+# SCHRITT 1: Die Klasse (der Bauplan) definieren
+# ==============================================================================
+class Fahrrad:
+    pass
+
+
+# ==============================================================================
+# SCHRITT 2: Eine Funktion, die Fahrräder beschreibt
+# ==============================================================================
+def fahrrad_info(bike):
+    # Wir greifen über den Punkt auf die Attribute des übergebenen Objekts zu:
+    return f"{bike.marke} ({bike.farbe}) - {bike.gaenge} Gänge - {bike.preis_pro_tag}€/Tag"
+
+
+# ==============================================================================
+# SCHRITT 3: Eine Funktion, die prüft, welches Fahrrad günstiger ist
+# ==============================================================================
+def guenstigeres_rad(rad_a, rad_b):
+    if rad_a.preis_pro_tag < rad_b.preis_pro_tag:
+        return rad_a
+    else:
+        return rad_b
+
+
+# ==============================================================================
+# SCHRITT 4: Wir erzeugen konkrete Objekte und probieren alles aus!
+# ==============================================================================
+if __name__ == "__main__":
+    # Erstes Fahrrad-Objekt erstellen:
+    mountainbike = Fahrrad()
+    mountainbike.marke = "Cube"
+    mountainbike.farbe = "Schwarz/Grün"
+    mountainbike.gaenge = 21
+    mountainbike.preis_pro_tag = 25.0
+
+    # Zweites Fahrrad-Objekt erstellen:
+    citybike = Fahrrad()
+    citybike.marke = "Gazelle"
+    citybike.farbe = "Himmelblau"
+    citybike.gaenge = 7
+    citybike.preis_pro_tag = 15.0
+
+    # Infos ausgeben:
+    print("Verfügbare Räder:")
+    print("1.", fahrrad_info(mountainbike))
+    print("2.", fahrrad_info(citybike))
+
+    # Günstigeres Rad finden:
+    schnaeppchen = guenstigeres_rad(mountainbike, citybike)
+    print(f"\nDas günstigere Rad ist das {schnaeppchen.marke} für nur {schnaeppchen.preis_pro_tag}€!")
+
+ +
+
💡 Was du hier sehen kannst:
+ mountainbike und citybike sind zwei getrennte Variablen im Arbeitsspeicher. Jedes hat seine eigenen Werte für .marke, .farbe und .preis_pro_tag, obwohl beide aus der Klasse Fahrrad stammen! +
+
+ + +
+

4. ⚠️ Häufige Stolperfallen für Einsteiger

+ +
+
1. Klammern beim Erstellen vergessen:
+ mein_rad = Fahrrad()  ✓ (Richtig: Erzeugt ein neues Objekt!)
+ mein_rad = Fahrrad  ✗ (Falsch: Speichert nur den Bauplan in die Variable!)
-
⚠️ Häufige Stolperfalle: Klassenname Großschreiben!
- In Python schreibt man Klassennamen standardmäßig in CamelCase mit großem Anfangsbuchstaben (z.B. Haustier, BankKonto), während Variablen und Funktionen klein geschrieben werden (z.B. mein_haustier, erstelle_tier()). +
2. Groß- und Kleinschreibung:
+ Klassennamen beginnen in Python immer mit einem Großbuchstaben (Haustier, Fahrrad). Variablen und Funktionsnamen schreibt man klein (bello, erstelle_bello()). +
+ +
+
3. 'return' in Funktionen vergessen:
+ Wenn eine Funktion ein Haustier bauen soll (wie erstelle_bello()), muss am Ende unbedingt return mein_tier stehen! Ohne return gibt Python automatisch None zurück.
- +
-

3. Deine Aufgaben in aufgabe.py

+

5. 🎯 Deine Aufgaben in aufgabe.py

-

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

+

Öffne die Datei 01_einstieg_klassen/aufgabe.py in deinem Editor und bearbeite die Aufgaben:

-

💡 Lösungshinweise (Aufklappen bei Bedarf)

+

💡 Lösungshinweise (Aufklappen, wenn du Hilfe brauchst)

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

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

-
if tier2.alter > tier1.alter:
-    return tier2
-else:
-    return tier1
+
def aelteres_tier(tier1, tier2):
+    if tier2.alter > tier1.alter:
+        return tier2
+    else:
+        return tier1
- +
-

4. Lösung überprüfen

-

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

+

6. 🧪 Lösung überprüfen

+

Öffne das Terminal und führe den Test aus:

Terminal diff --git a/02_init_und_self/README.md b/02_init_und_self/README.md index 52082c8..182ba03 100644 --- a/02_init_und_self/README.md +++ b/02_init_und_self/README.md @@ -16,64 +16,49 @@ Deshalb gibt es in Python den **Konstruktor**: `__init__`. `__init__` steht für *initialisieren* (vorbereiten/startklar machen). -Es ist eine **spezielle Methode**, die Python **automatisch aufruft**, sobald du ein neues Objekt erstellst: +Es ist eine **spezielle Methode**, die Python **automatisch aufruft**, sobald du ein neues Objekt erstellst. + +--- + +## 📱 Vollständiges Praxisbeispiel: Die Smartphone-Klasse ```python -class Haustier: - def __init__(self, name, tierart, alter): - self.name = name - self.tierart = tierart - self.alter = alter +class Smartphone: + # Der Konstruktor wird automatisch beim Erstellen aufgerufen: + def __init__(self, modell, speicher_gb, akku=100): + self.modell = modell # z.B. "iPhone 15" + self.speicher_gb = speicher_gb # z.B. 128 + self.akku = akku # Standardwert ist 100% -# Jetzt können wir die Werte direkt in die Klammern schreiben! -bello = Haustier("Bello", "Hund", 3) -mimi = Haustier("Mimi", "Katze", 5) + def benutzen(self, minuten): + verbrauch = minuten * 0.5 + if self.akku >= verbrauch: + self.akku -= verbrauch + return f"{self.modell}: {minuten} Min. genutzt. Akku: {self.akku:.0f}%" + else: + self.akku = 0 + return f"{self.modell}: Akku ist leer!" -print(bello.name) # Gibt sofort "Bello" aus + def aufladen(self): + self.akku = 100 + return f"{self.modell} ist wieder voll aufgeladen (100%)!" + +# Verwendung: +mein_handy = Smartphone("iPhone 15", 256) +altes_handy = Smartphone("Galaxy S10", 64, akku=40) + +print(mein_handy.benutzen(30)) # Verbraucht 15% -> 85% Rest +print(altes_handy.benutzen(90)) # Akku wird leer! +print(altes_handy.aufladen()) # Wieder 100% ``` -Die doppelten Unterstriche `__` nennt man in Python auch **Dunder** (Double Underscore). Sie signalisieren besondere Python-Funktionen. --- ## 🪞 Was bedeutet eigentlich `self`? -`self` verwirrt am Anfang fast jeden – ist aber ganz einfach: -- `self` bedeutet auf Deutsch **"ich selbst"** oder **"dieses Objekt hier"**. -- Wenn du `bello = Haustier("Bello", "Hund", 3)` aufrufst, übergibt Python das neu entstandene `bello`-Objekt automatisch als erstes Argument `self`. -- `self.name = name` bedeutet also: *"Speichere den übergebenen Namen in MEINEM EIGENEN Attribut `.name` ab!"* - -``` - Haustier("Bello", "Hund", 3) - │ - ▼ -def __init__(self, name, tierart, alter): - ▲ │ │ │ - │ ▼ ▼ ▼ - bello "Bello" "Hund" 3 - │ - ├── bello.name = "Bello" - ├── bello.tierart = "Hund" - └── bello.alter = 3 -``` - ---- - -## ⚙️ Standardwerte (Default Parameters) - -Manchmal hat ein Attribut einen typischen Startwert (z.B. ein neues Bankkonto startet meist bei `0.0` Euro): - -```python -class Bankkonto: - def __init__(self, inhaber, kontostand=0.0): - self.inhaber = inhaber - self.kontostand = kontostand - -# Konto mit 0.0 Euro Startguthaben: -k1 = Bankkonto("Anna") - -# Konto mit speziellem Startguthaben: -k2 = Bankkonto("Ben", 50.0) -``` +- `self` bedeutet auf Deutsch **"ich selbst"** oder **"dieses konkrete Objekt hier"**. +- Wenn du `mein_handy.benutzen(30)` aufrufst, übergibt Python das neu entstandene `mein_handy`-Objekt automatisch als erstes Argument `self`. +- `self.akku -= verbrauch` zieht den Strom genau vom Akku dieses einen Handys ab. --- diff --git a/02_init_und_self/index.html b/02_init_und_self/index.html index d9cc948..d6667b9 100644 --- a/02_init_und_self/index.html +++ b/02_init_und_self/index.html @@ -19,43 +19,36 @@
Kapitel 02

Der Konstruktor __init__ & das geheimnisvolle self 💳

-

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

+

Lerne, wie Objekte direkt beim Erstellen startklar gemacht werden – mit vollständigen Codebeispielen.

- +

1. Warum brauchen wir __init__?

- In Kapitel 01 mussten wir jedes Attribut mühsam von Hand setzen: + In Kapitel 01 mussten wir jedes Attribut mühsam nach dem Erstellen von Hand setzen:

bello = Haustier()
 bello.name = "Bello"
-bello.alter = 3  # Was passiert, wenn man hier das Alter vergisst? -> Programm stürzt ab!
+bello.alter = 3 # Wenn wir 100 Tiere haben, müssen wir hunderte Zeilen schreiben!

- Deshalb gibt es den Konstruktor __init__. Er wird von Python automatisch aufgerufen, sobald ein neues Objekt erstellt wird! + Die Lösung heißt Konstruktor: Die spezielle Methode __init__. Sie wird von Python automatisch aufgerufen, genau in dem Moment, in dem du ein Objekt erzeugst!

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

2. Was bedeutet self?

+

2. 📱 Vollständiges Beispiel: Die Smartphone-Klasse

-

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

+

Schau dir an, wie eine vollständige Klasse mit Konstruktor und Methoden in der Praxis aussieht:

@@ -64,44 +57,98 @@
- Python + Python (Komplettes Beispiel: smartphone.py)
-
class Bankkonto:
-    def __init__(self, inhaber, kontostand=0.0):
-        self.inhaber = inhaber          # Speichere 'inhaber' im Attribut DIESES Objekts
-        self.kontostand = kontostand    # Speichere 'kontostand' in MEINEM Speicher
+        
# 1. Die vollständige Klasse definieren:
+class Smartphone:
+    # Der Konstruktor wird automatisch beim Erstellen aufgerufen:
+    def __init__(self, modell, speicher_gb, akku=100):
+        self.modell = modell              # z.B. "iPhone 15"
+        self.speicher_gb = speicher_gb      # z.B. 128
+        self.akku = akku                  # Standardwert ist 100%
 
-# Verwendung:
-konto_anna = Bankkonto("Anna", 50.0)
-konto_ben = Bankkonto("Ben")          # Verwendet den Standardwert 0.0
+ # Eine Methode, die den Akkustand verringert: + def benutzen(self, minuten): + verbrauch = minuten * 0.5 + if self.akku >= verbrauch: + self.akku -= verbrauch + return f"{self.modell}: {minuten} Min. genutzt. Akku: {self.akku:.0f}%" + else: + self.akku = 0 + return f"{self.modell}: Akku ist leer!" + + # Eine Methode zum Aufladen: + def aufladen(self): + self.akku = 100 + return f"{self.modell} ist wieder voll aufgeladen (100%)!" + + +# ============================================================================== +# Jetzt verwenden wir die Smartphone-Klasse: +# ============================================================================== +if __name__ == "__main__": + # Handy 1 erstellen (nutzt Standard-Akku 100%): + mein_handy = Smartphone("iPhone 15", 256) + + # Handy 2 erstellen (mit 40% Start-Akku): + altes_handy = Smartphone("Galaxy S10", 64, akku=40) + + print(mein_handy.benutzen(30)) # Verbraucht 15% -> 85% Rest + print(altes_handy.benutzen(90)) # Akku wird leer! + print(altes_handy.aufladen()) # Wieder 100%
+
+
+ + +
+

3. Was bedeutet self ganz genau?

+ +

+ Das Wörtchen self steht für "dieses konkrete Objekt hier". +

+ +
+
🪞 Das Namensschild-Prinzip:
+ Wenn du mein_handy.benutzen(30) aufrufst, übergibt Python im Hintergrund automatisch mein_handy als erstes Argument self. +

+ self.akku -= verbrauch bedeutet: "Ziehe den Verbrauch vom Akku DIESES Handys ab – nicht von allen anderen Handys auf der Welt!"
💡 Warum self.inhaber = inhaber?
- Links vom Gleichheitszeichen steht self.inhaber – das ist die Variable, die am Objekt dauerhaft gespeichert bleibt. Rechts steht inhaber – das ist nur der temporäre Übergabeparameter der Funktion. +
    +
  • Links (self.inhaber): Das Attribut, das fest am Objekt im Arbeitsspeicher abgespeichert wird.
  • +
  • Rechts (inhaber): Der Wert, den jemand beim Aufruf in die Klammern übergeben hat.
  • +
- +
-

3. Häufige Stolperfallen beim Konstruktor

+

4. ⚠️ Häufige Fehler beim Konstruktor

-
⚠️ Fehler 1: Doppel-Unterstrich vs. Einzel-Unterstrich
- Es heißt __init__ mit zwei Unterstrichen davor und danach (Dunder), NICHT _init_! Wenn du nur einen Unterstrich schreibst, ignoriert Python die Methode einfach. +
1. Tippfehler bei den Unterstrichen:
+ def __init__(self):  ✓ (2 Unterstriche vorne, 2 hinten: Dunder!)
+ def _init_(self):  ✗ (Falsch! Python ruft die Methode nicht auf!)
-
⚠️ Fehler 2: self im Methodenkopf vergessen
- Jede Methode in einer Klasse muss als allerersten Parameter self haben: -
def einzahlen(self, betrag):  ✓ -
def einzahlen(betrag):  ✗ (Führt zu TypeError!) +
2. 'self' im Methodenkopf vergessen:
+ def einzahlen(self, betrag):  ✓
+ def einzahlen(betrag):  ✗ (Führt zu TypeError: takes 1 positional argument but 2 were given) +
+ +
+
3. Falsche Zuweisungsrichtung:
+ self.kontostand = kontostand  ✓
+ kontostand = self.kontostand  ✗ (Überschreibt den Parameter, speichert nichts am Objekt!)
- +
-

4. Deine Aufgaben in aufgabe.py

+

5. 🎯 Deine Aufgaben in aufgabe.py

Öffne die Datei 02_init_und_self/aufgabe.py:

@@ -135,7 +182,7 @@

💡 Lösungshinweise (Aufklappen bei Bedarf)

- Tipp zu TODO 3 (Auszahlen mit Absicherung) + 🔍 Tipp zu TODO 3 (Auszahlen mit Absicherung)
def auszahlen(self, betrag):
@@ -148,9 +195,9 @@
       
- +
-

5. Lösung überprüfen

+

6. 🧪 Lösung überprüfen

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

diff --git a/03_methoden_und_verhalten/README.md b/03_methoden_und_verhalten/README.md index 2883b3f..8be3140 100644 --- a/03_methoden_und_verhalten/README.md +++ b/03_methoden_und_verhalten/README.md @@ -6,53 +6,50 @@ Funktionen, die innerhalb einer Klasse definiert sind, nennt man **Methoden**. --- -## 🏃‍♀️ Was unterscheidet eine Methode von einer normalen Funktion? - -1. Eine Methode steht **eingerückt in der Klasse**. -2. Das **erste Argument ist immer `self`**. -3. Über `self` hat die Methode direkten Zugriff auf alle Eigenschaften des Objekts und kann diese verändern. +## 🏎️ Vollständiges Praxisbeispiel: Die Rennauto-Klasse ```python -class Zauberer: - def __init__(self, name, mana=50): - self.name = name - self.mana = mana +class Rennauto: + def __init__(self, fahrer, speed, tank=50): + self.fahrer = fahrer + self.speed = speed # z.B. 220 km/h + self.tank = tank # Aktueller Tank in Litern + self.max_tank = tank # Maximales Tankvolumen + self.gefahrene_km = 0 - # Das hier ist eine Methode: - def zauberspruch_wirken(self, mana_kosten): - if self.mana >= mana_kosten: - self.mana -= mana_kosten - print(f"{self.name} wirkt einen Zauber! Verbleibendes Mana: {self.mana}") - return True + def fahren(self, km): + verbrauch = km * 0.2 + if self.tank >= verbrauch: + self.tank -= verbrauch + self.gefahrene_km += km + return f"{self.fahrer} fährt {km} km (Tank: {self.tank:.1f}L übrig)" else: - print(f"{self.name} hat nicht genug Mana!") - return False + return f"{self.fahrer} hat nicht genug Sprit für {km} km!" -merlin = Zauberer("Merlin", 30) -merlin.zauberspruch_wirken(10) # self wird automatisch merlin! + def tanken(self, liter): + freier_platz = self.max_tank - self.tank + getankt = min(freier_platz, liter) + self.tank += getankt + return f"{self.fahrer} tankt {getankt:.1f} Liter nach" + + def duell_gegen(self, gegner_auto): + if self.speed > gegner_auto.speed: + return f"{self.fahrer} überholt {gegner_auto.fahrer}!" + else: + return f"{gegner_auto.fahrer} zieht an {self.fahrer} vorbei!" + +# Testen: +auto1 = Rennauto("Max", 240, 40) +auto2 = Rennauto("Lewis", 235, 50) +print(auto1.fahren(100)) +print(auto1.duell_gegen(auto2)) ``` --- ## 🤝 Wenn Objekte miteinander interagieren! -Das Spannendste an der objektorientierten Programmierung ist, wenn **ein Objekt mit einem anderen Objekt interagiert**. - -Du kannst ein ganzes Objekt als Parameter an die Methode eines anderen Objekts übergeben: - -```python -class Spieler: - def __init__(self, name, geld): - self.name = name - self.geld = geld - - def bezahle_an(self, empfaenger, betrag): - if self.geld >= betrag: - self.geld -= betrag - empfaenger.geld += betrag # Wir greifen direkt auf das andere Objekt zu! - return True - return False -``` +Du kannst ein ganzes Objekt als Parameter an die Methode eines anderen Objekts übergeben (wie bei `auto1.duell_gegen(auto2)` oder `ritter.angreifen(drache)`). --- @@ -68,10 +65,12 @@ class Spieler: ## 🎯 Deine Aufgabe: Das RPG-Duell -In `aufgabe.py` erstellst du eine Klasse `Held` für ein Rollenspiel: -- Ein Held hat `name`, `leben`, `max_leben` und `angriffskraft`. -- Ein Held kann Schaden erleiden (`schaden_erleiden`), geheilt werden (`heilen`) und einen anderen Helden angreifen (`angreifen(gegner)`). -- Du sorgst mit Bedingungen dafür, dass die Lebenspunkte nicht unter `0` fallen und nicht über `max_leben` steigen! +In `aufgabe.py` erstellst du die Klasse `Held`: +- `__init__(self, name, leben=100, angriffskraft=15)` +- `schaden_erleiden(self, schaden)` +- `heilen(self, heilung)` +- `angreifen(self, gegner)` +- `status_text(self)` ### Testen: ```bash diff --git a/03_methoden_und_verhalten/index.html b/03_methoden_und_verhalten/index.html index e501f41..dccdff5 100644 --- a/03_methoden_und_verhalten/index.html +++ b/03_methoden_und_verhalten/index.html @@ -19,20 +19,35 @@
Kapitel 03

Methoden & Verhalten: Wenn Objekte lebendig werden ⚔️

-

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

+

Lass Objekte aktiv handeln und miteinander interagieren anhand eines vollständigen Rennspiel- & Duell-Beispiels.

- +

1. Was sind Methoden?

- Bisher haben unsere Klassen vor allem Daten gespeichert. Aber in der objektorientierten Programmierung haben Objekte auch Verhalten. Funktionen, die zu einer Klasse gehören, nennt man Methoden. + Bisher haben unsere Klassen vor allem Daten gespeichert. Aber in der echten Programmierung haben Objekte auch Verhalten (sie können etwas tun). Funktionen, die innerhalb einer Klasse stehen, nennt man Methoden.

+
+
💡 Der Unterschied zwischen Funktion und Methode:
+
    +
  • Eine Funktion steht frei im Code: berechne_summe(a, b)
  • +
  • Eine Methode gehört zu einem Objekt und wird mit Punkt aufgerufen: mein_held.angreifen(gegner)
  • +
+
+
+ + +
+

2. 🏎️ Vollständiges Beispiel: Die Rennauto-Klasse

+ +

Hier siehst du eine vollständige Klasse mit Konstruktor, mehreren Methoden, Wertebegrenzung (Tankinhalt) und einer Renn-Simulation:

+
@@ -40,44 +55,76 @@
- Python + Python (Komplettes Beispiel: rennauto.py)
-
class Zauberer:
-    def __init__(self, name, mana=50):
-        self.name = name
-        self.mana = mana
+        
class Rennauto:
+    def __init__(self, fahrer, speed, tank=50):
+        self.fahrer = fahrer
+        self.speed = speed          # z.B. 220 km/h
+        self.tank = tank            # Aktueller Tank in Litern
+        self.max_tank = tank        # Maximales Tankvolumen
+        self.gefahrene_km = 0
 
-    # Eine Methode gehört dem Zauberer:
-    def zauberspruch_wirken(self, kosten):
-        if self.mana >= kosten:
-            self.mana -= kosten
-            return True
-        return False
+ # Methode 1: Fahren (verbraucht Benzin) + def fahren(self, km): + verbrauch = km * 0.2 # 0.2 Liter pro km + if self.tank >= verbrauch: + self.tank -= verbrauch + self.gefahrene_km += km + return f"{self.fahrer} fährt {km} km (Tank: {self.tank:.1f}L übrig)" + else: + return f"{self.fahrer} hat nicht genug Sprit für {km} km!" + + # Methode 2: Tanken (nicht über max_tank) + def tanken(self, liter): + freier_platz = self.max_tank - self.tank + getankt = min(freier_platz, liter) + self.tank += getankt + return f"{self.fahrer} tankt {getankt:.1f} Liter nach (Tank: {self.tank:.1f}/{self.max_tank}L)" + + # Methode 3: Interaktion mit einem anderen Auto + def duell_gegen(self, gegner_auto): + if self.speed > gegner_auto.speed: + return f"{self.fahrer} überholt {gegner_auto.fahrer} mit Leichtigkeit!" + elif self.speed < gegner_auto.speed: + return f"{gegner_auto.fahrer} zieht an {self.fahrer} vorbei!" + else: + return f"{self.fahrer} und {gegner_auto.fahrer} sind genau gleich schnell!" + + +# Ausprobieren: +if __name__ == "__main__": + auto1 = Rennauto("Max", speed=240, tank=40) + auto2 = Rennauto("Lewis", speed=235, tank=50) + + print(auto1.fahren(100)) # Verbraucht 20 Liter -> 20L übrig + print(auto1.duell_gegen(auto2)) # Max ist schneller als Lewis! + print(auto1.tanken(30)) # Tankt bis max 40L auf
- +
-

2. Wenn zwei Objekte miteinander interagieren

+

3. 🤝 Interaktion: Ein Objekt übergibt sich an ein anderes

- Besonders spannend wird es, wenn eine Methode eines Objekts ein anderes Objekt als Parameter bekommt (z.B. ritter.angreifen(drache)). + In der Methode angreifen(self, gegner) übergibst du das gegnerische Held-Objekt als Parameter:

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

+ 1. self ist der Ritter (mit self.angriffskraft).
+ 2. gegner ist der Drache.
+ 3. Der Ritter ruft: gegner.schaden_erleiden(self.angriffskraft) auf!
+ 4. Der Drache verliert Leben und prüft, ob er noch lebt.
- +
-

3. Deine Aufgaben in aufgabe.py

+

4. 🎯 Deine Aufgaben in aufgabe.py

  • @@ -89,25 +136,25 @@
@@ -115,23 +162,22 @@

💡 Lösungshinweise (Aufklappen bei Bedarf)

- Tipp zu TODO 3 (Heilung begrenzen) + 🔍 Tipp zu TODO 4 (Angriff)
-
def heilen(self, heilung):
-    if not self.ist_am_leben or heilung <= 0:
-        return 0
-    alter_wert = self.leben
-    self.leben = min(self.max_leben, self.leben + heilung)
-    return self.leben - alter_wert  # Wie viel wurde wirklich geheilt?
+
def angreifen(self, gegner):
+    if self.ist_am_leben and gegner.ist_am_leben:
+        gegner.schaden_erleiden(self.angriffskraft)
+        return True
+    return False
- +
-

4. Lösung überprüfen

+

5. 🧪 Lösung überprüfen

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

diff --git a/04_str_und_darstellung/README.md b/04_str_und_darstellung/README.md index 1e4026d..a78f169 100644 --- a/04_str_und_darstellung/README.md +++ b/04_str_und_darstellung/README.md @@ -19,43 +19,46 @@ Das ist kryptisch und hilft im Alltag nicht weiter. Aber Python hat dafür eine --- -## ✨ Die `__str__`-Methode - -Wann immer Python ein Objekt in einen Text umwandeln soll (z.B. bei `print(mein_objekt)` oder `str(mein_objekt)` oder in einem f-String `f"Das Buch: {mein_objekt}"`), schaut Python nach, ob deine Klasse eine `__str__`-Methode hat. - -Wenn ja, ruft Python sie automatisch auf: +## 🍕 Vollständiges Praxisbeispiel: Pizza & Pizzeria-Bestellung ```python -class Buch: - def __init__(self, titel, autor): - self.titel = titel - self.autor = autor +# 1. Klasse Pizza mit __str__: +class Pizza: + def __init__(self, name, preis, extra_kaese=False): + self.name = name + self.preis = preis + self.extra_kaese = extra_kaese + if extra_kaese: + self.preis += 1.50 def __str__(self): - # Muss immer einen STRING zurückgeben (return)! - return f"'{self.titel}' von {self.autor}" + kaese = " (mit Extra-Käse)" if self.extra_kaese else "" + return f"Pizza {self.name}{kaese} - {self.preis:.2f} €" -mein_buch = Buch("Harry Potter", "J.K. Rowling") -print(mein_buch) # Ausgabe: 'Harry Potter' von J.K. Rowling 🎉 -``` +# 2. Klasse Bestellung verwaltet Pizza-Objekte: +class Bestellung: + def __init__(self, kunden_name): + self.kunden_name = kunden_name + self.pizzen = [] ---- + def pizza_hinzufuegen(self, pizza): + self.pizzen.append(pizza) -## 🧺 Objekte in Listen verwalten + def gesamtpreis(self): + return sum(p.preis for p in self.pizzen) -Oft hat man Objekte, die eine Liste von *anderen* Objekten verwalten (z.B. ein Warenkorb, der Artikel enthält, oder ein Bus, der Fahrgäste enthält): + def rechnung_drucken(self): + zeilen = [f"=== RECHNUNG FÜR {self.kunden_name} ==="] + for p in self.pizzen: + zeilen.append(f"- {p}") # Ruft p.__str__() auf! + zeilen.append(f"Gesamt: {self.gesamtpreis():.2f} €") + return "\n".join(zeilen) -```python -class Schultasche: - def __init__(self): - self.buecher = [] # Startet mit einer leeren Liste - - def buch_einpacken(self, buch): - self.buecher.append(buch) - - def inhalt_anzeigen(self): - for b in self.buecher: - print(f"- {b}") # Hier wird automatisch b.__str__() genutzt! +# Testen: +bestellung = Bestellung("Lisa") +bestellung.pizza_hinzufuegen(Pizza("Margherita", 8.50)) +bestellung.pizza_hinzufuegen(Pizza("Salami", 9.50, extra_kaese=True)) +print(bestellung.rechnung_drucken()) ``` --- diff --git a/04_str_und_darstellung/index.html b/04_str_und_darstellung/index.html index 6fae5f0..0e558d5 100644 --- a/04_str_und_darstellung/index.html +++ b/04_str_und_darstellung/index.html @@ -19,18 +19,18 @@
Kapitel 04

Schöne Ausgaben mit __str__ & Objektlisten 🧾

-

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

+

Lerne anhand eines Pizzeria-Beispiels, wie du print(objekt) formatierst und Objekte in Listen sammelst.

- +
-

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

+

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

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

@@ -39,13 +39,20 @@

- Python weiß ohne deine Hilfe nicht, wie du dein Objekt darstellen möchtest. + Python weiß nicht, welche Attribute für einen Menschen interessant sind.

✨ Die Magie von __str__:
- Sobald du in deiner Klasse eine Methode def __str__(self): definierst, ruft Python diese automatisch auf, wann immer das Objekt zu Text umgewandelt wird (bei print(), str() oder in f-Strings)! + Sobald du in deiner Klasse die Methode def __str__(self): definierst, ruft Python diese automatisch auf, sobald das Objekt in Text umgewandelt werden soll (bei print(), str() oder in f-Strings f"{objekt}")!
+
+ + +
+

2. 🍕 Vollständiges Beispiel: Pizza & Pizzeria-Bestellung

+ +

Hier siehst du zwei vollständige Klassen, die zusammenarbeiten: Pizza (mit __str__) und Bestellung (die eine Liste von Pizzen verwaltet):

@@ -54,38 +61,81 @@
- Python + Python (Komplettes Beispiel: pizzeria.py)
-
class Buch:
-    def __init__(self, titel, autor):
-        self.titel = titel
-        self.autor = autor
+        
# ==============================================================================
+# KLASSE 1: Einzelne Pizza mit schöner __str__ Ausgabe
+# ==============================================================================
+class Pizza:
+    def __init__(self, name, preis, extra_kaese=False):
+        self.name = name
+        self.preis = preis
+        self.extra_kaese = extra_kaese
+        if extra_kaese:
+            self.preis += 1.50  # Extra Käse kostet 1.50€ mehr
 
+    # Diese Methode bestimmt, wie die Pizza als Text aussieht:
     def __str__(self):
-        return f"'{self.titel}' von {self.autor}"  # MUSS ein String sein!
+        kaese_hinweis = " (mit Extra-Käse 🧀)" if self.extra_kaese else ""
+        return f"Pizza {self.name}{kaese_hinweis} - {self.preis:.2f} €"
 
-mein_buch = Buch("Harry Potter", "J.K. Rowling")
-print(mein_buch)  # Ausgabe: 'Harry Potter' von J.K. Rowling
+ +# ============================================================================== +# KLASSE 2: Bestellung verwaltet eine Liste von Pizzen +# ============================================================================== +class Bestellung: + def __init__(self, kunden_name): + self.kunden_name = kunden_name + self.pizzen = [] # Startet mit einer leeren Liste + + def pizza_hinzufuegen(self, pizza): + self.pizzen.append(pizza) + + def gesamtpreis(self): + return sum(p.preis for p in self.pizzen) + + def rechnung_drucken(self): + zeilen = [f"=== RECHNUNG FÜR {self.kunden_name.upper()} ==="] + for p in self.pizzen: + zeilen.append(f"- {p}") # Hier wird automatisch p.__str__() aufgerufen! + zeilen.append("--------------------------------") + zeilen.append(f"Gesamtsumme: {self.gesamtpreis():.2f} €") + return "\n".join(zeilen) + + +# Ausprobieren: +if __name__ == "__main__": + bestellung = Bestellung("Lisa") + + p1 = Pizza("Margherita", 8.50) + p2 = Pizza("Salami", 9.50, extra_kaese=True) + + bestellung.pizza_hinzufuegen(p1) + bestellung.pizza_hinzufuegen(p2) + + print(bestellung.rechnung_drucken())
- +
-

2. Objekte in Listen verwalten

+

3. ⚠️ Wichtig bei __str__

-

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

+
+
1. Immer 'return', niemals 'print'!
+ def __str__(self): return f"..."  ✓
+ def __str__(self): print(f"...")  ✗ (Falsch! __str__ MUSS einen String mit return zurückgeben!) +
-
-
🛒 Der Warenkorb:
- Ein Warenkorb besitzt eine Liste self.artikel_liste = []. Wenn du einen Artikel hinzufügst, landet das ganze Artikel-Objekt in der Liste! +
+
2. Rückgabewert muss zwingend ein String sein:
+ Wenn du eine Zahl zurückgibst (z.B. return self.preis), stürzt Python ab mit TypeError: __str__ returned non-string. Nutze immer f-Strings f"{self.preis}" oder str(...).
- +
-

3. Deine Aufgaben in aufgabe.py

+

4. 🎯 Deine Aufgaben in aufgabe.py

  • @@ -103,7 +153,7 @@
  • @@ -117,7 +167,7 @@

    💡 Lösungshinweise (Aufklappen bei Bedarf)

    - Tipp zu TODO 7 (Kassenzettel zusammensetzen) + 🔍 Tipp zu TODO 7 (Kassenzettel zusammensetzen)
    def bon_text(self):
    @@ -132,9 +182,9 @@
           
- +
-

4. Lösung überprüfen

+

5. 🧪 Lösung überprüfen

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

diff --git a/05_objekte_kombinieren/README.md b/05_objekte_kombinieren/README.md index a5d4b46..4e626ec 100644 --- a/05_objekte_kombinieren/README.md +++ b/05_objekte_kombinieren/README.md @@ -4,66 +4,62 @@ In der echten Softwareentwicklung (wie bei Spotify, Instagram oder Netflix) best Wenn eine Klasse andere Objekte besitzt oder verwaltet, nennt man das **Komposition** (oder *"hat-ein"-Beziehung*): - Eine **Playlist** *hat viele* **Songs**. +- Eine **Schulklasse** *hat viele* **Schüler**. - Ein **Kino** *hat viele* **Säle** und **Filme**. -- Eine **Schule** *hat viele* **Klassen** und **Schüler**. --- -## 🎶 Das Zusammenspiel: Song & Playlist - -Schauen wir uns an, wie eine Playlist mit Songs arbeiten kann: +## 🎒 Vollständiges Praxisbeispiel: Der Schüler- & Notenmanager ```python -class Song: - def __init__(self, titel, dauer_sekunden): - self.titel = titel - self.dauer_sekunden = dauer_sekunden - -class Playlist: - def __init__(self, name): +# 1. Klasse Schueler +class Schueler: + def __init__(self, name, note): self.name = name - self.songs = [] # Hier landen die Song-Objekte + self.note = float(note) - def hinzufuegen(self, song): - self.songs.append(song) + def __str__(self): + return f"{self.name} (Note: {self.note:.1f})" - def finde_lange_songs(self, min_sekunden): - gefundene = [] - for song in self.songs: - if song.dauer_sekunden >= min_sekunden: - gefundene.append(song) - return gefundene +# 2. Klasse Schulklasse verwaltet viele Schüler-Objekte: +class Schulklasse: + def __init__(self, klassen_name): + self.klassen_name = klassen_name + self.schueler_liste = [] + + def hinzufuegen(self, schueler): + self.schueler_liste.append(schueler) + + def notendurchschnitt(self): + if not self.schueler_liste: + return 0.0 + summe = sum(s.note for s in self.schueler_liste) + return summe / len(self.schueler_liste) + +# Testen: +klasse_9a = Schulklasse("Klasse 9a") +klasse_9a.hinzufuegen(Schueler("Emma", 1.0)) +klasse_9a.hinzufuegen(Schueler("Lukas", 2.3)) +klasse_9a.hinzufuegen(Schueler("Mia", 1.3)) + +print("Durchschnitt:", klasse_9a.notendurchschnitt()) ``` --- ## ⏱️ Zeit-Formatierung: Aus Sekunden werden Minuten & Sekunden -Um z.B. `215` Sekunden als `"03:35"` darzustellen, teilt man mit Ganzzahldivision `//` und Modulo `%`: ```python -sekunden_gesamt = 215 -minuten = sekunden_gesamt // 60 # 3 -sekunden = sekunden_gesamt % 60 # 35 - -# Mit :02d wird immer zweistellig mit führender Null formatiert: -print(f"{minuten:02d}:{sekunden:02d}") # "03:35" +minuten = self.dauer_sekunden // 60 +sekunden = self.dauer_sekunden % 60 +return f"{minuten:02d}:{sekunden:02d}" # "03:35" ``` --- ## 🎯 Deine Aufgabe: Der Spotify-Playlist-Manager -In `aufgabe.py` baust du deine eigene Musikverwaltung: -1. **Klasse `Song`**: - - Attribute: `titel`, `kuenstler`, `dauer_sekunden` - - Methode: `formatierte_dauer()` -> `"MM:SS"` - - `__str__()` -> `"{titel}" von {kuenstler} ({MM:SS})` -2. **Klasse `Playlist`**: - - `song_hinzufuegen(song)` - - `anzahl_songs()` - - `gesamtdauer_sekunden()` - - `finde_songs_von(kuenstler)` (Groß-/Kleinschreibung ignorieren!) - - `laengster_song()` (liefert das Song-Objekt mit der längsten Dauer) +In `aufgabe.py` baust du deine eigene Musikverwaltung mit `Song` und `Playlist`. ### Testen: ```bash diff --git a/05_objekte_kombinieren/index.html b/05_objekte_kombinieren/index.html index 9f11649..90ba9f3 100644 --- a/05_objekte_kombinieren/index.html +++ b/05_objekte_kombinieren/index.html @@ -25,60 +25,111 @@
- +

1. Was ist Komposition?

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

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

2. Nützliche Python-Kniffe

+

2. 🎒 Vollständiges Beispiel: Schüler & Schulklasse Notenmanager

-

⏱️ Sekunden in "MM:SS" umrechnen

-

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

+

Hier siehst du ein vollständiges Beispiel aus dem Schulalltag: Die Klasse Schueler speichert Name und Note. Die Klasse Schulklasse verwaltet alle Schüler, berechnet den Notendurchschnitt und sucht die Klassenbesten:

- Python +
+ + + +
+ Python (Komplettes Beispiel: schulklasse.py)
-
gesamt_sekunden = 215
-minuten = gesamt_sekunden // 60   # 3 Minuten
-sekunden = gesamt_sekunden % 60   # 35 Restsekunden
+        
# ==============================================================================
+# KLASSE 1: Ein einzelner Schüler
+# ==============================================================================
+class Schueler:
+    def __init__(self, name, note):
+        self.name = name
+        self.note = float(note)
 
-# :02d sorgt für zweistellige Zahlen mit führender Null (z.B. 03:05 statt 3:5):
-zeit_text = f"{minuten:02d}:{sekunden:02d}"  # "03:35"
+ def __str__(self): + return f"{self.name} (Note: {self.note:.1f})" + + +# ============================================================================== +# KLASSE 2: Die Schulklasse verwaltet viele Schüler-Objekte +# ============================================================================== +class Schulklasse: + def __init__(self, klassen_name): + self.klassen_name = klassen_name + self.schueler_liste = [] + + def hinzufuegen(self, schueler): + self.schueler_liste.append(schueler) + + def notendurchschnitt(self): + if not self.schueler_liste: + return 0.0 + summe = sum(s.note for s in self.schueler_liste) + return summe / len(self.schueler_liste) + + def beste_schueler(self): + # Findet alle Schüler mit einer 1 (Note <= 1.5) + return [s for s in self.schueler_liste if s.note <= 1.5] + + +# Ausprobieren: +if __name__ == "__main__": + klasse_9a = Schulklasse("Klasse 9a") + + s1 = Schueler("Emma", 1.0) + s2 = Schueler("Lukas", 2.3) + s3 = Schueler("Mia", 1.3) + + klasse_9a.hinzufuegen(s1) + klasse_9a.hinzufuegen(s2) + klasse_9a.hinzufuegen(s3) + + print(f"Durchschnitt: {klasse_9a.notendurchschnitt():.2f}") # 1.53 + + print("\nBeste Schüler:") + for einserschueler in klasse_9a.beste_schueler(): + print(f"- {einserschueler}")
+
+
+ + +
+

3. Nützliche Kniffe für die Song-Aufgabe

+ +

⏱️ Sekunden in "MM:SS" umrechnen

+
+
minuten = self.dauer_sekunden // 60
+sekunden = self.dauer_sekunden % 60
+return f"{minuten:02d}:{sekunden:02d}"  # z.B. "03:35"

🔍 Groß- und Kleinschreibung beim Suchen ignorieren

-

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

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

3. Deine Aufgaben in aufgabe.py

+

4. 🎯 Deine Aufgaben in aufgabe.py

  • @@ -102,7 +153,7 @@
@@ -110,7 +161,7 @@

💡 Lösungshinweise (Aufklappen bei Bedarf)

- Tipp zum längsten Song finden + 🔍 Tipp zum längsten Song finden
def laengster_song(self):
@@ -127,9 +178,9 @@
       
- +
-

4. Lösung überprüfen

+

5. 🧪 Lösung überprüfen

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

diff --git a/06_abschlussprojekt_tamagotchi/README.md b/06_abschlussprojekt_tamagotchi/README.md index e8bd336..fca6c3f 100644 --- a/06_abschlussprojekt_tamagotchi/README.md +++ b/06_abschlussprojekt_tamagotchi/README.md @@ -7,24 +7,56 @@ Herzlichen Glückwunsch! Du hast alle grundlegenden Konzepte der objektorientier - `__str__` für ansprechende Textausgaben - Beziehungen zwischen Objekten -Jetzt setzen wir alles in einem **vollwertigen, interaktiven Mini-Projekt** zusammen: Einem virtuellen Haustier (Tamagotchi)! +--- + +## 🤖 Vollständiges Beispiel: Roboter mit Lebenszyklus + +```python +class Roboter: + def __init__(self, name): + self.name = name + self.energie = 100 + self.ist_aktiv = True + + def _begrenzen(self, wert): + return max(0, min(100, wert)) + + def arbeiten(self, stunden): + if not self.ist_aktiv: + return f"{self.name} ist ausgeschaltet!" + verbrauch = stunden * 20 + self.energie = self._begrenzen(self.energie - verbrauch) + if self.energie == 0: + self.ist_aktiv = False + return f"{self.name} schaltet sich ab!" + return f"{self.name} hat gearbeitet (Energie: {self.energie}%)" + + def aufladen(self): + self.energie = 100 + self.ist_aktiv = True + return f"{self.name} ist wieder voll!" + + def __str__(self): + status = "Aktiv ⚡" if self.ist_aktiv else "Ausgeschaltet 💤" + return f"[{self.name}] Energie: {self.energie}% | Status: {status}" + +# Testen: +bot = Roboter("Wall-E") +print(bot) +print(bot.arbeiten(3)) +print(bot) +print(bot.aufladen()) +``` --- ## 🎮 Wie funktioniert das Tamagotchi? -Dein Tamagotchi hat Eigenschaften, die sich durch deine Aktionen (und mit der Zeit) verändern: -- **Hunger** (0 = vollgefressen, 100 = verhungert) -- **Müdigkeit** (0 = voller Energie, 100 = total erschöpft) -- **Glück** (0 = unglücklich, 100 = überglücklich) +- **Hunger** (0 bis 100) +- **Müdigkeit** (0 bis 100) +- **Glück** (0 bis 100) - **Alter in Tagen** -Du kannst mit ihm interagieren: -- 🍎 **Füttern** (`fuettern`): Senkt den Hunger, macht etwas müde und etwas glücklicher. -- ⚽ **Spielen** (`spielen`): Steigert das Glück, macht aber hungrig und müde. Zu müde Haustiere weigern sich zu spielen! -- 💤 **Schlafen** (`schlafen`): Setzt die Müdigkeit auf 0, lässt einen Tag vergehen (`alter_tage += 1`), macht aber etwas hungrig. -- ⏳ **Zeit vergeht** (`zeit_vergeht`): Das Haustier wird hungriger, müder und etwas trauriger. Wenn der Hunger 100 erreicht oder Glück 0 wird, schläft es für immer ein... - --- ## 🕹️ Das interaktive Spiel @@ -34,7 +66,6 @@ Sobald du die `Tamagotchi`-Klasse in `aufgabe.py` fertig programmiert hast und a ```bash python3 tamagotchi_spiel.py ``` -Dort kannst du deinem Haustier einen Namen geben und dich live um es kümmern! --- diff --git a/06_abschlussprojekt_tamagotchi/index.html b/06_abschlussprojekt_tamagotchi/index.html index f2de1d3..f2a570a 100644 --- a/06_abschlussprojekt_tamagotchi/index.html +++ b/06_abschlussprojekt_tamagotchi/index.html @@ -19,73 +19,119 @@
Abschlussprojekt

Dein eigenes Tamagotchi (Virtuelles Haustier) 🥚🐣

-

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

+

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

- +

1. Das große Finale! 🏆

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

-

- Jetzt bauen wir ein komplettes, lebendiges Tamagotchi, das du im Terminal füttern, schlafen legen und mit dem du spielen kannst! + Du hast alle Grundlagen gemeistert: Klassen & Konstruktoren, Methoden, __str__ und Objekt-Beziehungen. Jetzt bauen wir ein komplettes, virtuelles Haustier (Tamagotchi), das du im Terminal füttern, schlafen legen und mit dem du spielen kannst!

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

2. 🤖 Vollständiges Beispiel: Ein Roboter mit Lebenszyklus

+ +

Bevor du das Tamagotchi baust, schau dir dieses vollständige Beispiel an. Es zeigt, wie Attribute begrenzt werden (Clamping 0-100) und wie ein Status überprüft wird:

+ +
+
+
+ + + +
+ Python (Komplettes Beispiel: roboter.py) +
+
class Roboter:
+    def __init__(self, name):
+        self.name = name
+        self.energie = 100          # 100% Start-Energie
+        self.ist_aktiv = True
+
+    # Hilfsmethode, um Werte zwischen 0 und 100 zu halten:
+    def _begrenzen(self, wert):
+        return max(0, min(100, wert))
+
+    def arbeiten(self, dauer_stunden):
+        if not self.ist_aktiv:
+            return f"{self.name} ist ausgeschaltet!"
+
+        verbrauch = dauer_stunden * 20
+        self.energie = self._begrenzen(self.energie - verbrauch)
+        
+        if self.energie == 0:
+            self.ist_aktiv = False
+            return f"{self.name} hat keine Energie mehr und schaltet sich ab!"
+        
+        return f"{self.name} hat {dauer_stunden}h gearbeitet. Restenergie: {self.energie}%"
+
+    def aufladen(self):
+        self.energie = 100
+        self.ist_aktiv = True
+        return f"{self.name} ist wieder voll aufgeladen!"
+
+    def __str__(self):
+        status = "Aktiv ⚡" if self.ist_aktiv else "Ausgeschaltet 💤"
+        return f"[{self.name}] Energie: {self.energie}% | Status: {status}"
+
+
+# Ausprobieren:
+if __name__ == "__main__":
+    bot = Roboter("Wall-E")
+    print(bot)
+    print(bot.arbeiten(3))  # Verbraucht 60% -> 40% übrig
+    print(bot.arbeiten(3))  # Energie auf 0 -> schaltet ab
+    print(bot)
+    print(bot.aufladen())
- +
-

2. Deine Aufgaben in aufgabe.py

+

3. 🎯 Deine Aufgaben in aufgabe.py

@@ -93,9 +139,9 @@

💡 Lösungshinweise (Aufklappen bei Bedarf)

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

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

+

Damit Werte nicht unter 0 fallen oder über 100 steigen, nutze max(0, min(100, wert)):

self.hunger = max(0, min(100, self.hunger - menge))
@@ -103,11 +149,11 @@
- +
-

3. Das interaktive Spiel starten! 🕹️

+

4. 🕹️ Das interaktive Spiel starten!

-

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

+

Sobald alle Unittests grün sind, kannst du das Spiel live spielen:

@@ -117,8 +163,8 @@
-
🎉 Geschafft!
- Du hast damit erfolgreich gelernt, wie man objektorientiert in Python programmiert! +
🎉 Du hast es geschafft!
+ Herzlichen Glückwunsch zum erfolgreichen Abschluss der Python OOP Übungsreihe!