Polymorphismus ist ein grundlegendes Prinzip der objektorientierten Programmierung, das es Objekten mit unterschiedlicher Struktur ermöglicht, dasselbe Interface zu implementieren, und zwar so, dass sie austauschbar arbeiten können. In Python ist der Polymorphismus aufgrund der dynamischen Typisierung ziemlich flexibel.
In Sprachen mit strenger statischer Typisierung (Java, C++) erfordert die Unterstützung von Polymorphismus die Deklaration von Interfaces oder abstrakten Klassen. In Python — dank des Duck Typing — ist Polymorphismus nicht an eine Vererbungshierarchie oder gar an ein Interface gebunden. Wichtig ist nur, dass das Objekt die benötigten Methoden/Attribute unterstützt.
Einerseits macht das die Sprache flexibel und benutzerfreundlich. Andererseits erhöht es die Wahrscheinlichkeit von Laufzeitfehlern aufgrund von Tippfehlern oder fehlenden benötigten Methoden, die sich erst bei der Ausführung des Codes zeigen.
In Python unterscheidet man:
class Animal: def speak(self): raise NotImplementedError class Dog(Animal): def speak(self): return 'Woof!' class Cat(Animal): def speak(self): return 'Meow!' def animal_voice(animal): print(animal.speak()) animal_voice(Dog()) # Woof! animal_voice(Cat()) # Meow!
class Duck: def quack(self): return 'Quack!' def make_quack(animal): print(animal.quack()) make_quack(Duck()) # Quack!
Wichtige Merkmale:
Ist Vererbung der einzige Weg, Polymorphismus in Python zu erreichen?
Nein. Dank des Duck Typing kann jede Funktion ein Objekt mit den benötigten Methoden unabhängig von seiner Klasse oder Hierarchie akzeptieren.
Kann man die Übereinstimmung der Signatur nur anhand des Methodennamens und nicht ihres Inhalts als Polymorphismus ansehen?
Nein. Wenn ein Objekt die benötigte Methode unterstützt, aber ihre Semantik von der erwarteten abweicht — können Bugs auftreten. Polymorphismus ist nur erfolgreich, wenn nicht nur das Interface, sondern auch die Bedeutung übereinstimmt.
Bietet eine abstrakte Klasse Schutz vor falschen Implementierungen von Interfaces in untergeordneten Klassen?
Nur teilweise. Wenn der Nachfolger die abstrakte Methode nicht implementiert — tritt ein TypeError auf, wenn versucht wird, eine Instanz zu erstellen. Wenn er sie jedoch mit Logikbrüchen implementiert — können Fehler auftreten.
Eine Logging-Bibliothek implementiert eine externe Klasse, die eine log()-Methode hat, aber diese gibt ein Datenobjekt zurück, anstatt in das Logbuch zu schreiben. Fehler treten erst bei der Nutzung auf.
Vorteile:
Nachteile:
Klassen werden mit Tests versehen, das Interface wird durch eine abstrakte Basisklasse und @abstractmethod formalisiert, und die Semantik der Methoden wird in der Dokumentation festgehalten.
Vorteile:
Nachteile: