Polymorphism is a fundamental principle of object-oriented programming that allows objects with different structures to implement the same interface, meaning they can work interchangeably. In Python, with dynamic typing, polymorphism is quite flexible.
In languages with strict static typing (Java, C++), support for polymorphism requires declaring interfaces or abstract classes. In Python, thanks to duck typing, polymorphism is not tied to the inheritance hierarchy or even an interface. The main requirement is that the object supports the necessary methods/attributes.
On one hand, this makes the language flexible and convenient. On the other hand, it increases the likelihood of runtime errors due to typos or the absence of a necessary method, which only manifest when the code is executed.
In Python, there are:
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!
Key features:
Is inheritance the only way to ensure polymorphism in Python?
No. Thanks to duck typing, any function can accept an object with the necessary methods, regardless of its class or hierarchy.
Can matching a signature by method name alone be considered polymorphism, rather than its essence?
No. If an object supports the needed method but its semantics differ from what is expected — bugs can occur. Polymorphism is successful only when both the interface and the meaning match.
Does an abstract class provide protection against errors of incorrectly implemented interfaces in child classes?
Only partially. If a descendant does not implement the abstract method — a TypeError will occur when trying to create an instance. But if it implements it with a logic violation — errors are possible.
An external class with a log() method is integrated into the logging library, but it returns a data object instead of writing to the log. Errors appear only during operation.
Pros:
Cons:
Classes are equipped with tests, the interface is formalized through an abstract base class and @abstractmethod, and the semantics of methods are documented.
Pros:
Cons: