Полиморфизм — фундаментальный принцип объектно-ориентированного программирования, который позволяет объектам с разной структурой реализовывать один и тот же интерфейс, а значит работать взаимозаменяемо. В Python с динамической типизацией полиморфизм устроен весьма гибко.
В языках со строгой статической типизацией (Java, C++) поддержка полиморфизма требует объявления интерфейсов или абстрактных классов. В Python — благодаря duck typing — полиморфизм не привязан к иерархии наследования и даже интерфейсу. Главное, чтобы объект поддерживал нужные методы/атрибуты.
С одной стороны, это делает язык гибким и удобным. С другой — увеличивает вероятность ошибок в рантайме из-за опечаток или отсутствия нужного метода, которые проявятся только при выполнении кода.
В Python различают:
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!
Ключевые особенности:
Является ли наследование единственным способом обеспечения полиморфизма в Python?
Нет. Благодаря duck typing любая функция может принимать объект с нужными методами независимо от его класса или иерархии.
Можно ли считаться полиморфизмом соответствие сигнатуре лишь по названию метода, а не его сути?
Нет. Если объект поддерживает нужный метод, но его семантика отличается от ожидаемой — возможны баги. Полиморфизм успешен только при совпадении не только интерфейса, но и смысла.
Обеспечивает ли абстрактный класс защиту от ошибок неправильно реализованного интерфейса в дочерних классах?
Лишь частично. Если наследник не реализует абстрактный метод — возникнет TypeError при попытке создать экземпляр. Но если реализует с нарушением логики — ошибки возможны.
В библиотеку логирования внедряют сторонний класс, у которого есть метод log(), но он возвращает объект данных вместо записи в журнал. Ошибки проявляются только при эксплуатации.
Плюсы:
Минусы:
Классы снабжаются тестами, интерфейс формализуется через абстрактный базовый класс и @abstractmethod, в документации прописана семантика методов.
Плюсы:
Минусы: