Python интерфейсы для документации - PullRequest
1 голос
/ 22 декабря 2019

Я понимаю, что в Python мы используем типизацию утки вместо реализации конкретного интерфейса.

Так что если у вас есть функция, которая принимает объект животного, который должен уметь есть и пить, вы можетепросто вызовите .eat() и .drink(), и любой передаваемый объект должен просто включать эти методы любым удобным для них способом.

Нет необходимости создавать интерфейс, который каждый реализует при вызове вашей функции.

Но ради документации и новых подсказок типов Python3, есть ли какой-нибудь правильный способ создания интерфейса.

Что-то вроде ...

interface Animal:
    eat()
    drink()

def foo(a: Animal):
    a.eat()
    a.drink()

... где интерфейс Animal предназначен исключительно для документации и неисполненной подсказки типа в функции foo. Он никогда не используется во время выполнения, и те, кто передают животных в функцию, все еще следуют протоколу утки.

Возможно ли это в Python?

1 Ответ

3 голосов
/ 22 декабря 2019

Для этого вы можете использовать ABC (абстрактные базовые классы). Базовому классу (читай «интерфейс») необходимо либо установить metaclass на abc.ABCMeta, либо наследовать от чего-то, что делает. Затем вы можете использовать декораторы, чтобы указать, что методы должны быть переопределены.

import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def eat(self):
        pass

    @abc.abstractmethod
    def drink(self):
        pass

def foo(a: Animal):
    a.eat()
    a.drink()

Свойства, методы класса и статические методы также можно сделать абстрактными с помощью обычных декораторов до @abc.abstractmethod:

@property
@abc.abstractmethod
def data(self):
    return self._data

@classmethod
@abc.abstractmethod
def abstract_classmethod(cls, ...):
    pass

@staticmethod
@abc.abstractmethod
def abstract_staticmethod(...):
    pass

Здесь используется ...

In [40]: class Animal(metaclass=ABCMeta):
    ...:     @abstractmethod
    ...:     def eat(self):
    ...:         print('Eats...')
    ...:     @abstractmethod
    ...:     def drink(self):
    ...:         print('Drinks...')
    ...:
    ...:

In [41]: def foo(a: Animal):
    ...:     a.eat()
    ...:

In [42]: class Elephant:
    ...:     def eat(self):
    ...:         print('Elephant eating...')
    ...:     def drink(self):
    ...:         print('Elephant drinking...')
    ...:


In [43]: class Giraffe(Animal):
    ...:     def eat(self):
    ...:         print('Giraffe eats...')
    ...:     def drink(self):
    ...:         print('Giraffe drinks...')
    ...:

In [44]: giraffe = Giraffe()

In [45]: elephant = Elephant()

In [46]: isinstance(giraffe, Animal)
Out[46]: True

In [47]: isinstance(elephant, Animal)
Out[47]: False

In [48]: foo
Out[48]: <function __main__.foo(a: __main__.Animal)>

In [49]: foo(giraffe)
Giraffe eats...

In [50]: foo(elephant)
Elephant eating...

In [51]: foo(Animal())
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-51-e060be6dff03> in <module>
----> 1 foo(Animal())

TypeError: Can't instantiate abstract class Animal with abstract methods drink, eat
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...