Полиморфизм в Python позволяет объектам разных классов реагировать на один и тот же метод по-разному, в зависимости от их типа. Это достигается за счет нескольких механизмов:
class Duck:
    def quack(self):
        return "Quack!"
class Cat:
    def quack(self):
        return "Meow!"
def make_sound(animal):
    print(animal.quack())
duck = Duck()
cat = Cat()
make_sound(duck)  # Output: Quack!
make_sound(cat)   # Output: Meow!
            
class Animal:
    def make_sound(self):
        return "Generic animal sound"
class Dog(Animal):
    def make_sound(self):
        return "Woof!"
class Cat(Animal):
    def make_sound(self):
        return "Meow!"
animal = Animal()
dog = Dog()
cat = Cat()
print(animal.make_sound()) # Output: Generic animal sound
print(dog.make_sound())    # Output: Woof!
print(cat.make_sound())    # Output: Meow!
            
from abc import ABC, abstractmethod
class Shape(ABC):
    @abstractmethod
    def area(self):
        pass
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    def area(self):
        return 3.14159 * self.radius * self.radius
class Square(Shape):
    def __init__(self, side):
        self.side = side
    def area(self):
        return self.side * self.side
circle = Circle(5)
square = Square(4)
print(circle.area())  # Output: 78.53975
print(square.area())  # Output: 16
# Trying to instantiate Shape directly would raise a TypeError:
# TypeError: Can't instantiate abstract class Shape with abstract methods area
            
def operate(x, y, operation):
    return operation(x, y)
def add(x, y):
    return x + y
def subtract(x, y):
    return x - y
result1 = operate(5, 3, add)  # Result: 8
result2 = operate(5, 3, subtract) # Result: 2
result3 = operate(5, 3, lambda x, y: x * y) # Result: 15
print(result1)
print(result2)
print(result3)
            В целом, Python отдает предпочтение duck typing как основному способу реализации полиморфизма, поскольку он делает код более гибким и менее связанным с иерархией классов. Абстрактные базовые классы используются, когда необходимо обеспечить определенный интерфейс и гарантировать наличие определенных методов в подклассах.