← Назад к вопросам

Является ли утиная типизация одним из типов полиморфизма в Python?

1.8 Middle🔥 241 комментариев
#Python Core#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Duck Typing как форма полиморфизма

Да, утиная типизация (duck typing) — это один из типов полиморфизма в Python. Это структурный полиморфизм, основанный на наличии методов и атрибутов, а не на явном типе.

Принцип Duck Typing

"Если это выглядит как утка, плывет как утка и крякает как утка — это утка"

Другими словами: объект определяется его поведением, а не типом.

# Классы с разными типами, но одинаковым интерфейсом
class Dog:
    def sound(self):
        return "Woof!"

class Cat:
    def sound(self):
        return "Meow!"

class Robot:
    def sound(self):
        return "Beep!"

# Функция не проверяет тип — просто вызывает sound()
def make_sound(animal):
    print(animal.sound())  # Duck typing!

# Все работают, хотя типы разные
make_sound(Dog())    # Woof!
make_sound(Cat())    # Meow!
make_sound(Robot())  # Beep!

Утиная типизация vs другие типы полиморфизма

1. Параметрический полиморфизм (Generics):

from typing import TypeVar, Generic

T = TypeVar('T')

class Box(Generic[T]):
    def __init__(self, value: T):
        self.value = value
    
    def get(self) -> T:
        return self.value

box_int = Box[int](42)
box_str = Box[str]("Hello")

2. Подтипизация (Subtype polymorphism — наследование):

class Animal:
    def make_sound(self):
        pass

class Dog(Animal):
    def make_sound(self):
        return "Woof"

class Cat(Animal):
    def make_sound(self):
        return "Meow"

# Подтипы заменяют базовый тип
def animal_voice(animal: Animal):
    print(animal.make_sound())

3. Duck Typing (структурный полиморфизм):

# Нет явного наследования, просто методы
class Bird:
    def fly(self):
        return "Flying high"

class Airplane:
    def fly(self):
        return "Flying faster"

# Обе могут летать — duck typing
def take_off(flyer):
    print(flyer.fly())

take_off(Bird())      # Работает
take_off(Airplane())  # Тоже работает

Преимущества Duck Typing

# 1. Гибкость — не нужно создавать иерархию классов
def process_iterable(collection):
    """Работает с любым объектом, имеющим __iter__"""
    for item in collection:
        print(item)

process_iterable([1, 2, 3])           # list
process_iterable((1, 2, 3))           # tuple
process_iterable({1, 2, 3})           # set
process_iterable(range(1, 4))         # range
process_iterable("123")               # string

# 2. Минимальная связанность
class FileReader:
    def read(self):
        return "file content"

class DatabaseReader:
    def read(self):
        return "db content"

def process_data(reader):  # Не важен тип!
    data = reader.read()
    print(f"Data: {data}")

process_data(FileReader())      # Работает
process_data(DatabaseReader())  # Тоже работает

Недостатки Duck Typing

1. Ошибки выявляются позже (в runtime):

class BadReader:
    def write(self):  # Ошибка: должен быть read!
        return "oops"

def process_data(reader):
    return reader.read()  # AttributeError в runtime!

process_data(BadReader())  # Ошибка здесь!

2. Сложность для IDE (автодополнение):

def process(obj):  # IDE не знает, какие методы у obj
    obj.???  # Автодополнение не поможет

Решение: Protocol (PEP 544)

Для типизации duck typing используются Protocol:

from typing import Protocol

class Reader(Protocol):  # Структурный интерфейс
    def read(self) -> str:
        ...

class FileReader:
    def read(self) -> str:
        return "file"

class HttpReader:
    def read(self) -> str:
        return "http"

def process_data(reader: Reader) -> None:  # Type hint
    data = reader.read()
    print(data)

# Работает, но тип-чекер (mypy) поймет структуру
process_data(FileReader())   # OK
process_data(HttpReader())   # OK
process_data(BadReader())    # ERROR - нет read()

Практический пример: Интеграция разных источников данных

from typing import Protocol

class DataSource(Protocol):
    """Любой источник данных, поддерживающий этот интерфейс"""
    def fetch(self) -> dict:
        ...
    def close(self) -> None:
        ...

class MySQLDataSource:
    def fetch(self):
        return {"data": "from mysql"}
    
    def close(self):
        print("MySQL closed")

class APIDataSource:
    def fetch(self):
        return {"data": "from api"}
    
    def close(self):
        print("API closed")

class DataProcessor:
    def process(self, source: DataSource) -> None:
        data = source.fetch()
        print(f"Processing: {data}")
        source.close()

# Использование
processor = DataProcessor()
processor.process(MySQLDataSource())   # Duck typing!
processor.process(APIDataSource())     # Тоже работает

Сравнение подходов

Без типизации (чистый duck typing):

def save(obj):
    return obj.save()  # Надеемся, что есть save()

Проблема: IDE не поможет, ошибки в runtime.

С наследованием (классический OOP):

from abc import ABC, abstractmethod

class Saveable(ABC):
    @abstractmethod
    def save(self):
        pass

class User(Saveable):
    def save(self):
        return "User saved"

def save_object(obj: Saveable):
    return obj.save()

Проблема: нужно явно наследоваться (жесткая иерархия).

С Protocol (лучший подход):

from typing import Protocol

class Saveable(Protocol):
    def save(self) -> str:
        ...

class User:
    def save(self):
        return "User saved"

def save_object(obj: Saveable):
    return obj.save()

# Работает, IDE помогает, тип-чекер проверяет

Заключение

Duck Typing — это структурный полиморфизм, основанный на:

  1. Наличии методов/атрибутов, а не типе
  2. Динамической проверке (EAFP — Easier to Ask for Forgiveness than Permission)
  3. Минимальной связанности между компонентами

Формула: если объект имеет нужные методы → его можно использовать

Модерный Python сочетает:

  • Duck typing для гибкости
  • Protocol для типизации
  • Type hints для автодополнения IDE

Это лучшее из обоих миров: гибкость динамической типизации + безопасность статического анализа.

Является ли утиная типизация одним из типов полиморфизма в Python? | PrepBro