Duck typing — это основополагающий принцип Python, по которому объект рассматривается по своему поведению, а не по принадлежности к иерархии классов.
Термин идёт из поговорки: "Если что-то выглядит как утка, плавает как утка и крякает как утка — значит, это утка". В Python поведение объекта (его интерфейс) важнее класса, к которому он принадлежит. Это реализует принцип "duck typing" — типизация по поведению (structural typing).
Кажется, будто duck typing даёт максимальную гибкость. Но это увеличивает количество скрытых багов: программа "ломается" только во время выполнения, если объект не поддерживает нужный интерфейс.
Вместо проверки типа (через isinstance или type) пишите функции, которые пробуют вызвать нужные методы у объекта, рассчитывая, что если он поддерживает их — всё сработает. В крайнем случае — отлавливайте AttributeError или TypeError, чтобы обработать неожиданные объекты.
Пример:
def quack_and_walk(duck): duck.quack() duck.walk() class Robot: def quack(self): print("I can quack!") def walk(self): print("I am walking") quack_and_walk(Robot()) # всё сработает!
Ключевые особенности:
Можно ли в Python проверить тип через isinstance и сказать, что это правильно для duck typing?
Нет. Duck typing как раз противопоставляется жесткой проверке типа. Корректнее оперировать поведением объекта, а не его родословной.
Можно ли реализовать duck typing с помощью абстрактных базовых классов (ABC)?
Частично. Абстрактные классы вводят элементы статической структуры. Duck typing не требует объявлять родство — вы должны просто реализовать нужные методы. Но с Python 3.8 появился модуль typing.Protocol, который приближает нас к структурной типизации.
Может ли duck typing работать с магическими методами (len, getitem и т.п.)?
Да. Если объект реализует нужный метод (len), его можно передать в функции, например, len(obj), и это будет работать по duck typing.
Разработчик пишет функцию, которая принимает всё с методом run(), без проверки. В коде появляется объект без этого метода — и ошибка возникает только в рантайме.
Плюсы:
Минусы:
Использование duck typing, но с try/except и документацией интерфейса:
def run_task(obj): try: obj.run() except AttributeError: print("Объект не поддерживает запуск задачи!")
Плюсы:
Минусы: