Kurs
Kurzer Überblick über Vererbung
Wenn deine Python-Projekte und -Pakete wachsen, willst du früher oder später Klassen nutzen und dabei das DRY-Prinzip (Don’t Repeat Yourself) anwenden. Klassenvererbung ist eine hervorragende Möglichkeit, eine Klasse auf Basis einer anderen zu erstellen und so Wiederholungen zu vermeiden. In diesem Beitrag geht es um fortgeschrittenere Konzepte der Vererbung; die Grundlagen streifen wir nur kurz. Für einen fundierten Einstieg empfehlen wir diese Ressourcen: Kurs Object-Oriented Programming in Python und das Python Object-Oriented Programming (OOP): Tutorial.
Was ist Klassenvererbung? Ähnlich wie in der Genetik kann eine Kindklasse Attribute und Methoden von einer Elternklasse „erben“. Schauen wir uns direkt ein Beispiel im Code an. Im folgenden Block vererbt eine Child-Klasse von einer Parent-Klasse.
Input
class Parent:
def __init__(self):
self.parent_attribute = 'I am a parent'
def parent_method(self):
print('Back in my day...')
# Create a child class that inherits from Parent
class Child(Parent):
def __init__(self):
Parent.__init__(self)
self.child_attribute = 'I am a child'
# Create instance of child
child = Child()
# Show attributes and methods of child class
print(child.child_attribute)
print(child.parent_attribute)
child.parent_method()
Output
I am a child
I am a parent
Back in my day...
Die Child-Klasse hat Attribute und Methoden der Parent-Klasse geerbt. Ohne weiteres Zutun gehört Parent.parent_method zur Child-Klasse. Um vom Parent.__init__() zu profitieren, mussten wir diese Methode explizit aufrufen und self übergeben. Grund: Sobald wir Child ein eigenes __init__ geben, überschreibt es das geerbte __init__.
Mit diesem kurzen Überblick genug der Vorrede – jetzt zum Kern des Beitrags.
Einführung in super
Im einfachsten Fall ersetzt die Funktion super den expliziten Aufruf Parent.__init__(self). Unser Einstiegsbeispiel aus dem ersten Abschnitt lässt sich mit super wie unten umschreiben. Hinweis: Der Codeblock ist für Python 3 geschrieben; frühere Versionen verwenden leicht andere Syntax. Die Ausgabe ist identisch mit dem ersten Block und daher weggelassen.
class Parent:
def __init__(self):
self.parent_attribute = 'I am a parent'
def parent_method(self):
print('Back in my day...')
# Create a child class that inherits from Parent
class Child(Parent):
def __init__(self):
super().__init__()
self.child_attribute = 'I am a parent'
# Create instance of child
child = Child()
# Show attributes and methods of child class
print(child.child_attribute)
print(child.parent_attribute)
child.parent_method()
Ehrlich gesagt bringt super in diesem Fall kaum Vorteile. Je nach Name der Elternklasse sparst du ein paar Tastenanschläge, und du musst self nicht an __init__ übergeben. Hier ein paar Vor- und Nachteile von super bei einfacher Vererbung.
Nachteile
Man kann argumentieren, dass super den Code weniger explizit macht. Das widerspricht dem Zen of Python: "Explicit is better than implicit."
Vorteile
Es gibt ein Wartungsargument zugunsten von super – auch bei einfacher Vererbung. Ändert sich die Vererbung deiner Kindklasse (z. B. andere Elternklasse oder Umstieg auf Mehrfachvererbung), musst du keine verteilten Verweise auf ParentClass.method_name() suchen und ersetzen; dank super greifen alle Änderungen automatisch über die class-Definition.
Meistere deine Datenkenntnisse mit DataCamp
Mehr als 10 Millionen Menschen lernen Python, R, SQL und andere technische Fertigkeiten in unseren praxisorientierten Kursen, die von Branchenexperten entwickelt wurden.

super und Mehrfachvererbung
Bevor wir in Mehrfachvererbung und super einsteigen ... Achtung: Das kann schnell knifflig werden.
Was ist Mehrfachvererbung? Bisher hatte unser Beispiel eine Kindklasse mit genau einer Elternklasse. Bei Mehrfachvererbung gibt es mehrere Elternklassen. Eine Kindklasse kann also von 2, 3, 10 usw. Eltern erben.
Hier werden die Vorteile von super deutlicher. Neben weniger Tippaufwand beim Referenzieren unterschiedlicher Elternklassen gibt es subtile Vorteile im Zusammenspiel von super und Mehrfachvererbung. Kurz gesagt: Wenn du Mehrfachvererbung nutzt, nutze super.
Mehrfachvererbung ohne super
Schauen wir uns ein Beispiel an, das keine Elternmethoden verändert und damit ohne super auskommt.
Input
class B:
def b(self):
print('b')
class C:
def c(self):
print('c')
class D(B, C):
def d(self):
print('d')
d = D()
d.b()
d.c()
d.d()
Output
b
c
d
Method Resolution Order
Diese Ausgabe überrascht kaum: D erbt die Methoden x und z von den Elternklassen – alles gut ... vorerst.
Was aber, wenn B und C jeweils eine Methode mit demselben Namen hätten? Dann greift das Konzept der Method Resolution Order (MRO). Die MRO einer Kindklasse legt fest, wo Python nach einer Methode sucht und welche Methode bei Namenskonflikten aufgerufen wird.
Ein Beispiel:
Input
class B:
def x(self):
print('x: B')
class C:
def x(self):
print('x: C')
class D(B, C):
pass
d = D()
d.x()
print(D.mro())
Output
x: B
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
Beim Aufruf der geerbten Methode x sehen wir nur die Ausgabe aus B. Die MRO unserer Klasse D lässt sich mit der Klassenmethode mro anzeigen. Aus D.mro() lernen wir: Standardmäßig versucht das Programm Methoden in D zu finden, dann in B, danach in C und zuletzt in object. Wird sie dort nirgends gefunden, wirft Python einen Fehler, dass D die angefragte Methode nicht hat.
Wichtig: Jede Klasse erbt standardmäßig von object; es steht am Ende jeder MRO.
Mehrfachvererbung, super und das Diamond-Problem
Unten ein Beispiel, wie super die MRO bei __init__ sinnvoll nutzt. Wir erstellen eine Reihe von Textverarbeitungsklassen und kombinieren ihre Funktionalität in einer weiteren Klasse mittels Mehrfachvererbung. Es gibt 4 Klassen; die Vererbungsstruktur folgt dem Diagramm unten.
Hinweis: Die Struktur dient nur der Veranschaulichung. Ohne Einschränkungen gäbe es bessere Implementierungen.

Das ist ein Beispiel für das sogenannte Diamond-Problem der Mehrfachvererbung. Der Name kommt von der Form – und davon, dass das Ganze ziemlich verwirrend sein kann.
Die Struktur im Code, mithilfe von super:
Input
class Tokenizer:
"""Tokenize text"""
def __init__(self, text):
print('Start Tokenizer.__init__()')
self.tokens = text.split()
print('End Tokenizer.__init__()')
class WordCounter(Tokenizer):
"""Count words in text"""
def __init__(self, text):
print('Start WordCounter.__init__()')
super().__init__(text)
self.word_count = len(self.tokens)
print('End WordCounter.__init__()')
class Vocabulary(Tokenizer):
"""Find unique words in text"""
def __init__(self, text):
print('Start init Vocabulary.__init__()')
super().__init__(text)
self.vocab = set(self.tokens)
print('End init Vocabulary.__init__()')
class TextDescriber(WordCounter, Vocabulary):
"""Describe text with multiple metrics"""
def __init__(self, text):
print('Start init TextDescriber.__init__()')
super().__init__(text)
print('End init TextDescriber.__init__()')
td = TextDescriber('row row row your boat')
print('--------')
print(td.tokens)
print(td.vocab)
print(td.word_count)
Output
Start init TextDescriber.__init__()
Start WordCounter.__init__()
Start init Vocabulary.__init__()
Start Tokenizer.__init__()
End Tokenizer.__init__()
End init Vocabulary.__init__()
End WordCounter.__init__()
End init TextDescriber.__init__()
--------
['row', 'row', 'row', 'your', 'boat']
{'boat', 'your', 'row'}
5
Zunächst sehen wir: TextDescriber erbt alle Attribute der Klassenhierarchie. Dank Mehrfachvererbung können wir die Funktionalität mehrerer Klassen „kombinieren“.
Betrachten wir nun die Ausgaben aus den jeweiligen __init__-Methoden:
Jede __init__-Methode wurde genau einmal aufgerufen.
TextDescriber erbt von zwei Klassen, die beide von Tokenizer erben. Warum wurde Tokenizer.__init__ nicht doppelt aufgerufen?
Wenn wir alle super-Aufrufe durch die „alte“ Schreibweise ersetzen, gäbe es zwei Aufrufe von Tokenizer.__init__. super „denkt“ die Hierarchie mit und überspringt die doppelte Initialisierung.
Jede __init__-Methode startete, bevor die vorherige fertig war.
Die Start- und Endreihenfolge der __init__-Aufrufe ist wichtig, falls du ein Attribut mit gleichem Namen wie eine andere Elternklasse setzt. Dieses würde überschrieben – das kann schnell verwirren.
In unserem Fall vermeiden wir Namenskonflikte bei geerbten Attributen, daher funktioniert alles wie erwartet.
Nochmals: Das Diamond-Problem kann schnell komplex werden und zu unerwarteten Ergebnissen führen. Wie so oft in der Programmierung gilt: Komplizierte Designs möglichst vermeiden.
Was wir gelernt haben
- Wir haben die Funktion
superkennengelernt und gesehen, wie sie bei einfacher VererbungParentName.methodersetzen kann – oft wartungsfreundlicher. - Wir haben Mehrfachvererbung kennengelernt und wie sich die Funktionalität mehrerer Elternklassen in einer Kindklasse bündeln lässt.
- Wir haben die Method Resolution Order (MRO) kennengelernt und verstanden, wie sie bei Namenskonflikten in der Mehrfachvererbung entscheidet.
- Wir haben das Diamond-Problem betrachtet und gesehen, wie
superdie Navigation im Diamanten steuert.