После нескольких попыток нахождения забавного повествования, который держит для всей статьи и несчастной неудачу, я решил пойти с техническими частями в одиночку. Хватит моих коллег, нашел это интересно, поэтому я думаю, он удержит без шуток.
Python дает нам несколько способов проверить, что объекты, которые мы передаем к функциям, являются типов, которые мы ожидаем. Каждый метод имеет свои преимущества и недостатки.
Просто не заботясь
Первый вариант, естественно, не заботится о типах – просто напишите свой код и надеюсь на лучшее. Это жизнеспособный метод и часто используется. Особенно подходит для коротких фрагментов или сценариев, которые вы не ожидаете поддерживать многое. Это просто работает без накладных расходов.
def poke(duck): duck.quack() duck.walk()
Наследование
Другой вариант, как общий в языках ООП, состоит в том, чтобы использовать наследство. Мы можем определить Анас
класс, и ожидайте, что все его производные должны быть достаточно утиными.
class Anas: def quack(self): pass def walk(self): pass class Duck(Anas): def quack(self): print('Quack!') def walk(self): print('Walks like duck.') class Mallard(Anas): def quack(self): print('Quack!') def walk(self): print('Walks like duck.') def poke(duck): assert isinstance(duck, Anas)
Интерфейсы
В то время как наследование вроде добивается работа, роботизированная утка определенно не родом Анас , хотя он выполняет все характеристики, которые мы заботимся о. Поэтому вместо иерархического наследования мы можем использовать интерфейсы.
from abc import ABC, abstractmethod class IDuck(ABC): @abstractmethod def quack(self): pass @abstractmethod def walk(self): pass class Duck(IDuck): def quack(self): print('Quack!') def walk(self): print('Walks like duck.') class RoboticDuck(IDuck): def quack(self): print('Quack!') def walk(self): print('Walks like duck.') def poke(duck): assert isinstance(duck, IDuck)
Здорово. И если мы не контролируем типы, мы всегда можем писать адаптеры.
Тест утки
Но это Питон. Мы можем сделать лучше.
Как мы знаем, Python использует набрав уток. Таким образом, мы должны быть в состоянии использовать Тест утки для типов. В нашем примере каждый объект реализует Квалак ()
и Прогулка ()
это утка. Это достаточно легко, чтобы проверить.
def is_a_duck(duck): for attr in ('quack', 'walk'): if not hasattr(duck, attr): return False return True def poke(duck): assert is_a_duck(duck) duck.quack() duck.walk()
Это работает. Но мы перечислим Isinstance (...)
вызов. Мы, безусловно, можем сделать лучше.
Метаклассы и подклассные крючки
Метаклассы являются замечательными конструкциями. Поскольку их имя может предложить, они принимают участие в строительстве занятий. Они даже позволяют нам установить крючки в основные механизмы Python, такие как Isinstance (...)
, используя __subclasshook__
Отказ
from abc import ABC def is_a_duck(duck): for attr in ('quack', 'walk'): if not hasattr(duck, attr): return False return True class DuckChecker(ABC): @classmethod def __subclasshook__(cls, C): if cls is not DuckChecker: return NotImplemented return is_a_duck(C) def poke(duck): assert isinstance(duck, DuckChecker) duck.quack() duck.walk()
И мы вернемся к бизнесу. Что сказал, IS_A_DUCK
По-прежнему напечатанный беспорядок, и будет очень больно поддерживать.
Не было бы неплохо, если бы мы могли просто использовать наши Iduck
Интерфейс для проверки на укуса?
Абстрактные методы, опять же!
Счастливчик за нас – мы можем!
Среди прочего, ABC
Родительский класс перечисляет все @abstractmethod
S и хранит их в __abstractmethods__
Переменная участника. Это означает, что мы можем легко перечислить их в нашем подклассе и проверять их.
from abc import ABC, abstractmethod class IDuck(ABC): @abstractmethod def quack(self): pass @abstractmethod def walk(self): pass @classmethod def __subclasshook__(cls, C): if cls is not IDuck: return NotImplemented for attr in cls.__abstractmethods__: if not hasattr(C, attr): return False return True class Duck: def quack(self): print('Quack!') def walk(self): print('Walks like a duck.') def poke(duck): assert isinstance(duck, IDuck) duck.quack() duck.walk() poke(Duck()) # Quack! # Walks like a duck.
Потрясающий. Следующий шаг – разделяя интерфейс от проверки логики.
Протоколы
Чтение через Python Documentation и номенклатуру, вы могли бы увидеть термин «протокол» здесь и там. Это способ Python, чтобы вызвать набранные утки интерфейсы. Таким образом, вы могли бы сказать, что мы только что создали «Протокол Checker». Теперь мы можем разделить его в базовый класс.
from abc import ABC, abstractmethod class Protocol(ABC): _is_protocol = True @classmethod def __subclasshook__(cls, C): if not cls._is_protocol: return NotImplemented for attr in cls.__abstractmethods__: if not hasattr(C, attr): return False return True class IDuck(Protocol): @abstractmethod def quack(self): pass @abstractmethod def walk(self): pass
И это все. Что мало _is_protocol
Флаг там зря. Обычно мы проверим протокол-несс, используя Isinstance (...)
Отказ В этом случае, однако, мы подключаемся к этому механизму, и это приведет к бесконечному рекурсии.
Теперь мы можем использовать наши Протокол
Базовый класс свободно для создания новых протоколов по мере необходимости, с дружелюбным интерфейсным синтаксисом. Мы почти закончили.
Эта собака – утка
В некоторых случаях проверки протокола не могут быть то, что мы хотим. Очевидные причины, приходящие к уму, являются:
- Мы не можем проверить желаемую семантику, используя трюк протокола.
- Мы хотим разрушить хаос.
Для тех случаев (ну, в основном для первого) ABC
Базовый класс обеспечивает еще один трюк. Вместо определения __subclasshook__
Чтобы проверить интерфейс, мы можем простые зарегистрированные классы как действительные, «виртуальные подклассы».
from abc import ABC class IDuck(ABC): pass class Duck: def quack(self): print('Quack!') def walk(self): print('Walk like a duck.') IDuck.register(Duck) def poke(duck): assert isinstance(duck, IDuck) duck.quack() duck.walk()
Помните, что этот метод ставит все давление на программист. Писать Iduck.register (собака)
Это эквивалент заявления «Я поручил эту собаку, чтобы быть уткой». Это может пройти проверку, но не обязательно принесет желаемые результаты.
Резюме
В этой статье мы накрыли несколько способов проверки «утки» объектов. От принадлежности к роду Анаса просто разместил наклейку на голову, говоря «утки!». Некоторые из этих методов более полезны или применимы, чем другие, но я все еще думаю, что стоит знать со всеми из них. Кроме того, есть много тем, которые не покрываются здесь, как проверка статического типа.
Дальнейшее чтение
Методы метакласс, продемонстрированные здесь, упрощенные версии кода из ABC
и Набрав
модули. Я настоятельно рекомендую пройти через эти модули и их соответствующие документы, по крайней мере, сразу, чтобы расширить свои знания и покрывать любые отверстия, оставленные моими ручными волнистыми объяснениями.
Оригинал: “https://dev.to/tmr232/recognizing-ducks-n4c”