Полиморфизм: переопределение методов и работа с разными типами
Полиморфизм — это магия Python! ✨
Представь, что ты пишешь функцию, которая умеет работать с числами, строками, списками — и для каждого типа делает что-то своё, но выглядит это как один и тот же вызов. Это и есть полиморфизм в действии!
print(len("Привет")) # 6 (символов)
print(len([1, 2, 3])) # 3 (элемента)
Одна функция len(), но разное поведение для разных типов данных. 🔄
Переопределение методов: когда наследники умнее родителей 🧬
В ООП полиморфизм часто проявляется через переопределение методов — дочерние классы могут менять поведение методов, унаследованных от родителя.
class Animal:
def make_sound(self):
print("Какой-то звук")
class Cat(Animal):
def make_sound(self): # Переопределили метод!
print("Мяу!")
class Dog(Animal):
def make_sound(self): # И здесь тоже!
print("Гав!")
animals = [Cat(), Dog()]
for animal in animals:
animal.make_sound() # Один вызов — разное поведение
Вывод:
Мяу!
Гав!
Каждый объект знает, какую версию метода выполнить — это и есть полиморфное поведение. 🎭
Duck Typing: если ходит как утка... 🦆
В Python важна не столько принадлежность к классу, сколько наличие нужных методов. Это называют "утиной типизацией":
class Car:
def move(self):
print("Еду на колёсах")
class Boat:
def move(self):
print("Плыву по волнам")
def travel(vehicle):
vehicle.move() # Нам важно только наличие метода move()
travel(Car()) # Еду на колёсах
travel(Boat()) # Плыву по волнам
Объекты могут быть любого типа — главное, чтобы поддерживали ожидаемое поведение. Данила Бежин на своём YouTube-канале называет это "договором между объектами". 🤝
Магические методы и полиморфизм 🔮
Перегрузка операторов через магические методы — классический пример полиморфизма:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # Переопределяем сложение
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
Теперь оператор + работает с нашими векторами! Аналогично можно переопределять *, == и другие операторы.
Практический пример: универсальный обработчик данных 🛠️
Допустим, мы пишем функцию для сохранения данных в разных форматах:
class JSONSerializer:
def serialize(self, data):
import json
return json.dumps(data)
class XMLSerializer:
def serialize(self, data):
return f"<data>{data}</data>" # Упрощённый пример
def save_data(serializer, data):
print(f"Сохранение: {serializer.serialize(data)}")
save_data(JSONSerializer(), {"key": "value"})
save_data(XMLSerializer(), "some_data")
Вывод:
Сохранение: {"key": "value"}
Сохранение: <data>some_data</data>
Функция save_data работает с любым объектом, у которого есть метод serialize() — полиморфизм в чистом виде! 💎
Когда полиморфизм особенно полезен 🌟
1. Работа с коллекциями разных типов:
def total_length(items):
return sum(len(item) for item in items)
print(total_length(["abc", [1, 2], (9, 8, 7)])) # 6
2. Плагины и расширения — когда нужно поддерживать разные реализации.
3. API-интерфейсы — один метод, разное поведение для разных входных данных.
Вместо заключения: полиморфизм вокруг нас 🌍
Каждый раз, когда ты используешь + и для чисел, и для строк, или видишь, как print() корректно выводит любой объект — это полиморфизм. Он делает код:
- Гибким 🤸
- Расширяемым 🏗️
- Интуитивно понятным 🧠
Попробуй применить эти принципы в своём следующем проекте!