Polimorfizm — fundamentalna zasada programowania obiektowego, która pozwala obiektom o różnej strukturze realizować ten sam interfejs, a zatem działać wymiennie. W Pythonie z dynamiczną typizacją polimorfizm jest bardzo elastyczny.
W językach o ścisłej statycznej typizacji (Java, C++) wsparcie dla polimorfizmu wymaga deklaracji interfejsów lub klas abstrakcyjnych. W Pythonie — dzięki duck typing — polimorfizm nie jest związany z hierarchią dziedziczenia, a nawet z interfejsem. Główne, aby obiekt wspierał potrzebne metody/atrybuty.
Z jednej strony sprawia to, że język jest elastyczny i wygodny. Z drugiej — zwiększa prawdopodobieństwo błędów w czasie wykonywania spowodowanych literówkami lub brakiem potrzebnej metody, które ujawniają się tylko podczas wykonywania kodu.
W Pythonie wyróżnia się:
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!
Kluczowe cechy:
Czy dziedziczenie jest jedynym sposobem zapewnienia polimorfizmu w Pythonie?
Nie. Dzięki duck typing każda funkcja może akceptować obiekt z wymaganymi metodami niezależnie od jego klasy czy hierarchii.
Czy można uważać za polimorfizm zgodność sygnatury tylko na podstawie nazwy metody, a nie jej treści?
Nie. Jeśli obiekt wspiera potrzebną metodę, ale jej semantyka różni się od oczekiwanej — mogą wystąpić błędy. Polimorfizm jest skuteczny tylko przy zgodności nie tylko interfejsu, ale i sensu.
Czy klasa abstrakcyjna chroni przed błędami niewłaściwie zaimplementowanego interfejsu w klasach potomnych?
Tylko częściowo. Jeśli potomek nie zaimplementuje abstrakcyjnej metody — wystąpi TypeError przy próbie utworzenia instancji. Ale jeśli zaimplementuje z naruszeniem logiki — błędy są możliwe.
Do biblioteki logowania wprowadzono zewnętrzną klasę, która ma metodę log(), ale zwraca obiekt danych zamiast zapisu w dzienniku. Błędy ujawniają się tylko w trakcie eksploatacji.
Zalety:
Wady:
Klasy są zaopatrzone w testy, interfejs jest formalizowany przez abstrakcyjną klasę bazową i @abstractmethod, w dokumentacji zapisana jest semantyka metod.
Zalety:
Wady: