Полиморфизм (от греч. “много форм”) — это способность объектов разных классов обрабатывать один и тот же вызов метода по-разному. Это позволяет писать более гибкий и обобщенный код, работающий с объектами на основе их общего интерфейса, а не конкретной реализации.
5.1. Концепция Полиморфизма
Представьте, что у вас есть разные типы животных (Dog
, Cat
), и все они умеют издавать звук (speak
). Вы можете создать список животных и попросить каждое издать звук, не зная заранее, собака это или кошка. Каждое животное выполнит метод speak
по-своему.
class Animal: # Родительский класс
def __init__(self, name): self.name = name
def speak(self): raise NotImplementedError
class Dog(Animal): # Дочерний
def speak(self): return f"{self.name} говорит Гав!"
class Cat(Animal): # Дочерний
def speak(self): return f"{self.name} говорит Мяу!"
class Cow(Animal): # Дочерний
def speak(self): return f"{self.name} говорит Мууу!"
# Функция, работающая с любым Animal (полиморфизм)
def make_animal_speak(animal_obj):
print(animal_obj.speak())
animals = [Dog("Рекс"), Cat("Барсик"), Cow("Буренка")]
for animal in animals:
make_animal_speak(animal)
# Вывод:
# Рекс говорит Гав!
# Барсик говорит Мяу!
# Буренка говорит Мууу!
Функция make_animal_speak
не зависит от конкретного типа животного, ей важно лишь наличие метода speak
.
5.2. Утиная Типизация (Duck Typing)
Python во многом полагается на концепцию “утиной типизации”. Название происходит от фразы: “Если нечто крякает как утка и плавает как утка, то это, вероятно, и есть утка”.
В контексте Python это означает, что тип объекта определяется не столько его классом или наследованием, сколько наличием у него необходимых методов и атрибутов. Если объект может выполнить требуемое действие (имеет нужный метод), то его можно использовать в данном контексте.
class Duck:
def quack(self): print("Кря!")
def swim(self): print("Плывет")
class Person:
def quack(self): print("Я человек, крякающий как утка!")
def swim(self): print("Человек плывет")
def make_it_quack_and_swim(thing):
# Не важно, какого класса thing, главное - есть методы
try:
thing.quack()
thing.swim()
except AttributeError:
print("Это не умеет крякать и плавать!")
d = Duck()
p = Person()
make_it_quack_and_swim(d)
# Вывод:
# Кря!
# Плывет
make_it_quack_and_swim(p)
# Вывод:
# Я человек, крякающий как утка!
# Человек плывет
Функция make_it_quack_and_swim
успешно работает с объектами обоих классов, потому что оба имеют методы quack
и swim
.
5.3. Перегрузка Операторов (Operator Overloading)
Это форма полиморфизма, при которой стандартные операторы Python (+
, -
, *
, len()
и др.) могут быть переопределены для работы с объектами пользовательских классов. Это достигается путем реализации специальных (магических) методов
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
# Перегрузка оператора +
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
return NotImplemented # Важно для корректной работы
# Как объект будет представлен в виде строки
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 4)
v2 = Vector(1, 5)
v3 = v1 + v2 # Используем оператор + благодаря __add__
print(v3) # Вывод: Vector(3, 9)
5.4. Переопределение Методов (как форма полиморфизма)
Как мы видели в разделе 4. Наследование, переопределение методов — это когда дочерний класс предоставляет свою реализацию метода родителя. Это также является проявлением полиморфизма: объекты дочерних классов по-разному реагируют на вызов одного и того же метода, унаследованного от родителя.
〰〰〰 𓆝 𓆟 𓆞 𓆝 𓆟 𓆝 𓆟 𓆞 〰〰〰