Kapitel 10 • Schulabgleich 25.0

Vererbung (Inheritance) & super() 🧬🚗

Lerne, wie Klassen Eigenschaften und Methoden an Unterklassen vererben, Code-Duplikate verschwinden und wie super().__init__() funktioniert.

1. Was ist Vererbung? (Das DRY-Prinzip)

In vielen Software-Projekten gibt es Klassen, die sich sehr ähnlich sind. Stelle dir eine Fahrzeugflotte vor: Ein Auto, ein ElektroAuto und ein Lkw haben alle eine Marke, ein Modell, ein Baujahr, einen Grundpreis und können fahren.

Ohne Vererbung müsstest du all diese Variablen und Methoden in jeder einzelnen Klasse kopieren. Das verletzt eine der wichtigsten Regeln der Programmierung: DRY (Don't Repeat Yourself)!

🧬 Die DNA- & Stammbaum-Analogie:
Wie in der Biologie: Kinder erben Merkmale (Augenfarbe, Blutgruppe) von ihren Eltern, entwickeln aber auch ihre ganz eigenen Fähigkeiten (z.B. Klavierspielen oder Fußballspielen).

In Python ist Fahrzeug die Elternklasse (Basisklasse) und Auto die Kindklasse (abgeleitete Klasse). Das Auto erbt automatisch alles, was ein Fahrzeug kann, und fügt eigene Besonderheiten wie sitzplaetze und hupen() hinzu!
🔍 "Ist-ein" (Vererbung) vs. "Hat-ein" (Komposition):
  • Vererbung (Kapitel 10): Ein Auto ist ein Fahrzeug. Ein Elektroauto ist ein Auto.
  • Komposition (Kapitel 05): Ein Auto hat einen Motor. Eine Playlist hat viele Songs.

2. 📊 UML-Klassendiagramm: Die Vererbungshierarchie

Im UML-Diagramm wird Vererbung durch einen Pfeil mit geschlossener Dreiecksspitze (▲) dargestellt. Der Pfeil zeigt immer von der Kindklasse nach oben zur Elternklasse:

Fahrzeug (Basisklasse)
- marke: str
- modell: str
- baujahr: int
- grundpreis: float
- kilometerstand: float = 0.0
+ __init__(marke, modell, baujahr, grundpreis)
+ fahren(km: float) -> None
+ berechne_restwert(jahr: int) -> float
+ info() -> str
Auto
- sitzplaetze: int = 5
- anzahl_tueren: int = 5
+ __init__(..., sitzplaetze, tueren)
+ hupen() -> str
+ info() -> str (überschrieben)
Lkw
- max_zuladung_kg: float
- aktuelle_ladung_kg: float = 0.0
+ __init__(..., max_zuladung_kg)
+ beladen(kg: float) -> bool
+ entladen(kg: float) -> float
+ info() -> str (überschrieben)
ElektroAuto (erbt von Auto)
- batterie_kapazitaet_kwh: float
- batterie_ladestand_kwh: float
- verbrauch_pro_100km: float = 18.0
+ __init__(..., batterie_kapazitaet_kwh, ...)
+ reichweite() -> float
+ fahren(km: float) -> bool (mit Stromverbrauch)
+ aufladen(kwh: float) -> float
+ info() -> str (überschrieben)

3. Syntax: Wie funktioniert super() in Python?

Schritt 1: Vererbung deklarieren

Um von einer Klasse zu erben, schreibst du den Namen der Elternklasse in Klammern hinter den Klassennamen:

Python (Vererbungssyntax)
class Auto(Fahrzeug):   # Auto erbt alle Attribute und Methoden von Fahrzeug!
    pass

Schritt 2: Der Aufruf von super().__init__(...)

Wenn die Kindklasse einen eigenen Konstruktor __init__ besitzt, überschreibt sie zunächst den Eltern-Konstruktor. Damit die Eltern-Attribute (marke, modell, etc.) trotzdem sauber initialisiert werden, rufen wir mit super().__init__() den Konstruktor der Elternklasse auf:

Python (super().__init__ in Aktion)
class Auto(Fahrzeug):
    def __init__(self, marke: str, modell: str, baujahr: int, grundpreis: float, sitzplaetze: int = 5):
        # 1. Eltern-Konstruktor aufrufen (setzt marke, modell, baujahr, grundpreis, kilometerstand):
        super().__init__(marke, modell, baujahr, grundpreis)
        
        # 2. Zusätzliche, eigene Attribute der Kindklasse setzen:
        self.sitzplaetze: int = sitzplaetze
⚠️ Häufiger Anfängerfehler: super().__init__() vergessen
Wenn du in der Kindklasse super().__init__() vergisst, existieren Attribute wie self.marke oder self.kilometerstand am Kind-Objekt überhaupt nicht! Der Code stürzt beim ersten Zugriff mit einem AttributeError ab.

4. Typ-Prüfung: isinstance() und issubclass()

Weil ein ElektroAuto von Auto und dieses von Fahrzeug erbt, ist ein Elektroauto gleichzeitig eine Instanz aller drei Klassen:

Python (Typ-Prüfungen)
tesla = ElektroAuto("Tesla", "Model 3", 2023, 42000.0, batterie_kapazitaet_kwh=60.0)

print(isinstance(tesla, ElektroAuto))  # True
print(isinstance(tesla, Auto))         # True
print(isinstance(tesla, Fahrzeug))     # True
print(isinstance(tesla, Lkw))          # False (Lkw ist ein Geschwisterzweig!)

print(issubclass(ElektroAuto, Fahrzeug)) # True

5. 🎯 Deine Aufgaben in aufgabe.py

Öffne die Datei 10_vererbung_und_super/aufgabe.py und vervollständige die 4 Klassen:

💡 Lösungshinweise (Aufklappen bei Bedarf)

🔍 Tipp zu TODO 3 (Restwert berechnen)
alter = max(0, aktuelles_jahr - self.baujahr)
wertverlust = self.grundpreis * 0.05 * alter
restwert = self.grundpreis - wertverlust
return max(self.grundpreis * 0.10, restwert)
🔍 Tipp zu TODO 10 (ElektroAuto fahren)
if km <= 0:
    return False
benoetigt = (km / 100.0) * self.verbrauch_pro_100km
if self.batterie_ladestand_kwh >= benoetigt:
    self.batterie_ladestand_kwh -= benoetigt
    super().fahren(km)  # Erhöht den Kilometerstand in der Basisklasse!
    return True
return False

6. 🧪 Lösung überprüfen

Führe im Terminal im Ordner 10_vererbung_und_super folgenden Befehl aus:

Terminal
python3 test_aufgabe.py