W3docs

Python-Vererbung

Python-Vererbung verstehen: Unterklassen, Methodenüberschreibung, super(), Mehrfachvererbung, MRO und isinstance – mit ausführbaren Beispielen.

Python-Vererbung verstehen

Vererbung ermöglicht es Ihnen, eine neue Klasse auf Basis einer bestehenden aufzubauen. Die neue Klasse (die Unterklasse oder Kindklasse) erhält automatisch alle Attribute und Methoden der bestehenden Klasse (der Oberklasse, Basisklasse oder Elternklasse). Sie können dann zusätzliches Verhalten hinzufügen oder das Geerbte selektiv überschreiben.

Dieses Kapitel behandelt:

  • Die Syntax zur Erstellung von Unterklassen
  • Wie super() funktioniert und warum Sie es verwenden sollten
  • Methodenüberschreibung und der Aufruf der Elternversion
  • Einfache, mehrstufige und Mehrfachvererbung
  • Pythons Method Resolution Order (MRO)
  • isinstance() und issubclass() für Laufzeit-Typprüfungen
  • Häufige Fallstricke

Bevor Sie dieses Kapitel lesen, stellen Sie sicher, dass Sie mit Python-Klassen und -Objekten vertraut sind. Weitere OOP-Konzepte finden Sie unter Python Encapsulation und Python Abstract Classes.

Die Syntax der Vererbung

Um eine Unterklasse zu erstellen, schreiben Sie den Namen der Elternklasse in Klammern hinter den neuen Klassennamen:

class ParentClass:
    pass

class ChildClass(ParentClass):
    pass

Ein minimales, aber konkretes Beispiel mit einer Vehicle-Basisklasse und einer Car-Unterklasse:

class Vehicle:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def start(self):
        print(f"{self.make} {self.model} started.")

    def stop(self):
        print(f"{self.make} {self.model} stopped.")


class Car(Vehicle):
    def __init__(self, make, model, year, num_doors=4):
        super().__init__(make, model, year)   # delegate to Vehicle.__init__
        self.num_doors = num_doors


camry = Car("Toyota", "Camry", 2023)
camry.start()   # Toyota Camry started.
camry.stop()    # Toyota Camry stopped.
print(camry.num_doors)  # 4

Car erbt start() und stop() von Vehicle, ohne sie zu duplizieren. Das Hinzufügen von num_doors ist die einzige neue Aufgabe, die Car übernehmen muss.

super() verwenden

super() gibt ein Proxy-Objekt zurück, das Methodenaufrufe an die Elternklasse delegiert. Am häufigsten wird es innerhalb von __init__ verwendet, um die Attribute der Elternklasse zu initialisieren, bevor die eigenen Attribute der Kindklasse hinzugefügt werden:

class Car(Vehicle):
    def __init__(self, make, model, year, num_doors=4):
        super().__init__(make, model, year)  # runs Vehicle.__init__
        self.num_doors = num_doors

Warum ist super() dem direkten Aufruf Vehicle.__init__(self, ...) vorzuziehen?

  • Wartbarkeit: Wenn Sie die Elternklasse umbenennen oder ersetzen, müssen Sie nur eine Zeile ändern.
  • Mehrfachvererbung: super() folgt Pythons Method Resolution Order (MRO), sodass jede Klasse in der Kette korrekt aufgerufen wird. Das direkte Kodieren des Elternnamens überspringt diese Logik.

Sie können super() für jede Methode aufrufen, nicht nur für __init__:

class Car(Vehicle):
    def start(self):
        super().start()                               # call Vehicle.start first
        print(f"({self.num_doors}-door model ready)")

Methodenüberschreibung

Eine Unterklasse überschreibt eine Elternmethode, indem sie eine Methode mit demselben Namen definiert. Python ruft immer zuerst die am stärksten abgeleitete Version auf:

class Vehicle:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def start(self):
        print(f"{self.make} {self.model} started.")

    def stop(self):
        print(f"{self.make} {self.model} stopped.")


class Car(Vehicle):
    def start(self):
        print(f"{self.make} {self.model} revved the engine and started.")


camry = Car("Toyota", "Camry", 2023)
camry.start()  # Toyota Camry revved the engine and started.
camry.stop()   # Toyota Camry stopped.  (inherited, not overridden)

Wenn Sie das Verhalten der Elternklasse erweitern statt es zu ersetzen möchten, rufen Sie super() innerhalb der Überschreibung auf:

class Car(Vehicle):
    def start(self):
        super().start()                         # Vehicle.start runs first
        print("Seatbelt reminder: buckle up!")  # then add the extra step

Vererbung von Klassenattributen

Vererbung gilt auch für Klassenattribute (die instanzübergreifend geteilt werden). Eine Unterklasse kann sie auf dieselbe Weise überschreiben wie Methoden:

class Vehicle:
    wheels = 4

class Motorcycle(Vehicle):
    wheels = 2   # override the class attribute

class Car(Vehicle):
    pass         # inherits wheels = 4


print(Motorcycle.wheels)  # 2
print(Car.wheels)         # 4
print(Vehicle.wheels)     # 4

Arten der Vererbung

Einfachvererbung

Ein Kind, ein Elternteil — das häufigste Muster.

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} makes a sound."


class Dog(Animal):
    def speak(self):
        return f"{self.name} says woof!"


dog = Dog("Rex")
print(dog.speak())  # Rex says woof!

Mehrstufige Vererbung

Eine Unterklasse kann selbst wieder als Elternklasse dienen und so eine Kette bilden:

class Vehicle:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def stop(self):
        print(f"{self.make} {self.model} stopped.")


class Car(Vehicle):
    def __init__(self, make, model, year, num_doors=4):
        super().__init__(make, model, year)
        self.num_doors = num_doors

    def start(self):
        print(f"{self.make} {self.model} started.")


class ElectricCar(Car):
    def __init__(self, make, model, year, range_km):
        super().__init__(make, model, year)   # calls Car.__init__
        self.range_km = range_km

    def start(self):
        print(f"{self.make} {self.model} powered up silently.")

    def charge(self):
        print(f"Charging... range is {self.range_km} km.")


tesla = ElectricCar("Tesla", "Model 3", 2024, 580)
tesla.start()   # Tesla Model 3 powered up silently.
tesla.charge()  # Charging... range is 580 km.
tesla.stop()    # Tesla Model 3 stopped.  (inherited from Vehicle)

ElectricCar befindet sich zwei Ebenen unterhalb von Vehicle. Jeder Aufruf von super().__init__ steigt eine Ebene in der Kette auf, sodass alle drei __init__-Methoden ausgeführt werden.

Mehrfachvererbung

Python ermöglicht es einer Klasse, von mehr als einer Elternklasse zu erben. Listen Sie diese in den Klammern durch Kommas getrennt auf:

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} makes a sound."


class Swimmer:
    def swim(self):
        return f"{self.name} swims."


class Flyer:
    def fly(self):
        return f"{self.name} flies."


class Duck(Animal, Swimmer, Flyer):
    def speak(self):
        return f"{self.name} says quack!"


donald = Duck("Donald")
print(donald.speak())  # Donald says quack!
print(donald.swim())   # Donald swims.
print(donald.fly())    # Donald flies.

Mehrfachvererbung ist mächtig, aber komplex. Verwenden Sie Mixins — kleine, zweckgebundene Klassen, die eine einzige Fähigkeit hinzufügen —, um die Übersichtlichkeit zu wahren. Swimmer und Flyer oben sind genau dieses Muster.

Die Method Resolution Order (MRO)

Wenn Python eine Methode oder ein Attribut sucht, folgt es einer deterministischen Suchreihenfolge, der sogenannten Method Resolution Order (MRO). Bei einfacher Vererbung ist die Reihenfolge offensichtlich (Kind → Elternteil → Großelternteil → object). Bei Mehrfachvererbung verwendet Python den C3-Linearisierungsalgorithmus, um eine eindeutige Reihenfolge zu erzeugen.

Die MRO einer beliebigen Klasse kann mit .__mro__ oder help() überprüft werden:

print(Duck.__mro__)
# (<class 'Duck'>, <class 'Animal'>, <class 'Swimmer'>, <class 'Flyer'>, <class 'object'>)

Die MRO ist besonders wichtig, wenn mehrere Elternklassen dieselbe Methode definieren. Python wählt die erste Klasse in der MRO, die sie definiert. Deshalb ist super() in Mehrfachvererbungshierarchien so wichtig: Jede Klasse in der MRO ruft super() auf, sodass jede Klasse in der Kette die Möglichkeit bekommt, ausgeführt zu werden.

class Base:
    def greet(self):
        print("Hello from Base")


class Left(Base):
    def greet(self):
        print("Hello from Left")
        super().greet()


class Right(Base):
    def greet(self):
        print("Hello from Right")
        super().greet()


class Child(Left, Right):
    def greet(self):
        print("Hello from Child")
        super().greet()


Child().greet()
# Hello from Child
# Hello from Left
# Hello from Right
# Hello from Base

Jeder Aufruf von super() folgt der MRO (Child → Left → Right → Base), sodass jedes greet() genau einmal ausgeführt wird, obwohl Base sowohl Elternteil von Left als auch von Right ist. Dies ist das Diamantproblem — Pythons MRO löst es sauber.

Vererbung zur Laufzeit prüfen

isinstance(obj, cls)

Gibt True zurück, wenn obj eine Instanz von cls oder einer ihrer Unterklassen ist:

tesla = ElectricCar("Tesla", "Model 3", 2024, 580)

print(isinstance(tesla, ElectricCar))  # True
print(isinstance(tesla, Car))          # True  — Car is a parent
print(isinstance(tesla, Vehicle))      # True  — Vehicle is a grandparent
print(isinstance(tesla, str))          # False

Dies ist zuverlässiger als der Vergleich type(obj) == Car, der bei Unterklassen False zurückgibt.

issubclass(sub, cls)

Gibt True zurück, wenn sub eine Unterklasse von cls ist (einschließlich sich selbst):

print(issubclass(ElectricCar, Vehicle))  # True
print(issubclass(Car, ElectricCar))      # False
print(issubclass(Car, Car))              # True  — a class is a subclass of itself

Häufige Fallstricke

Vergessen, super().__init__() aufzurufen

Wenn das __init__ der Kindklasse super().__init__() nicht aufruft, werden die Attribute der Elternklasse nie gesetzt. Jede Methode, die diese Attribute erwartet, wirft einen AttributeError:

class Car(Vehicle):
    def __init__(self, make, model, year, num_doors=4):
        # forgot super().__init__(...)
        self.num_doors = num_doors

c = Car("Toyota", "Camry", 2023)
c.start()  # AttributeError: 'Car' object has no attribute 'make'

Überschreiben ohne super(), obwohl Erweiterung beabsichtigt war

Wenn Sie eine Methode überschreiben und super() vergessen, wird die Version der Elternklasse nie ausgeführt. Dadurch geht Verhalten still verloren, von dem anderer Code möglicherweise abhängt.

Tiefe Mehrfachvererbungshierarchien

Mehr als zwei Ebenen der Mehrfachvererbung sind sehr schwer nachzuvollziehen. Wenn Sie sich beim Schreiben von class Foo(A, B, C, D) wiederfinden, erwägen Sie stattdessen Komposition — das Speichern von Instanzen als Attribute.

Vorteile der Vererbung

  1. Wiederverwendung von Code — gemeinsame Logik lebt an einem Ort; Unterklassen erben sie kostenlos.
  2. Erweiterbarkeit — Sie können Verhalten in einer Unterklasse hinzufügen oder ändern, ohne die Elternklasse zu berühren, sodass bestehende Aufrufer unverändert bleiben.
  3. Polymorphismus — Funktionen, die ein Vehicle akzeptieren, funktionieren gleichermaßen mit jedem Car, Motorcycle oder ElectricCar. Siehe Python Polymorphism für das vollständige Bild.

Übungen

Übung
Which of the following statements about Python inheritance are correct according to the information provided on the specified URL?
Which of the following statements about Python inheritance are correct according to the information provided on the specified URL?
Was this page helpful?