Kapitel 04

Schöne Ausgaben mit __str__ & Objektlisten 🧾

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

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

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

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

Python weiß nicht, welche Attribute für einen Menschen interessant sind.

✨ Die Magie von __str__:
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):

Python (Komplettes Beispiel: pizzeria.py)
# ==============================================================================
# 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):
        kaese_hinweis = " (mit Extra-Käse 🧀)" if self.extra_kaese else ""
        return f"Pizza {self.name}{kaese_hinweis} - {self.preis:.2f} €"


# ==============================================================================
# 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())

3. ⚠️ Wichtig bei __str__

1. Immer 'return', niemals 'print'!
def __str__(self): return f"..."  ✓
def __str__(self): print(f"...")  ✗ (Falsch! __str__ MUSS einen String mit return zurückgeben!)
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(...).

4. 🎯 Deine Aufgaben in aufgabe.py

💡 Lösungshinweise (Aufklappen bei Bedarf)

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

5. 🧪 Lösung überprüfen

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

Terminal
python3 test_aufgabe.py