Когда следует наследовать от ABC? - PullRequest
0 голосов
/ 06 мая 2019

Я всегда думал, что нужно наследовать от abc.ABC, если не требуется, чтобы класс создавался.Но я только что понял, что если у класса есть @abstractmethod, то его нельзя также создать.

Есть ли еще какие-либо причины наследовать от ABC?

Ответы [ 2 ]

5 голосов
/ 06 мая 2019

Если вы не используете abc.ABCMeta в качестве метакласса для вашего класса (явно или наследуя от abc.ABC), использование abstractmethod на самом деле ничего не делает.

>>> from abc import abstractmethod, ABC
>>> class Foo:
...   @abstractmethod
...   def bar(self):
...     pass
...
>>> f = Foo()
>>>

Аналогично, использование ABCMeta мало что значит, если вы не пометите хотя бы один метод как абстрактный:

>>> class Bar(ABC):
...     pass
...
>>> b = Bar()
>>>

Это комбинация двух, которая позволяет (номинально) классу быть нереализуемым:

>>> class Baz(ABC):
...   @abstractmethod
...   def m(self):
...     pass
...
>>> b = Baz()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Baz with abstract methods m
>>>

(Даже в этом случае обратите внимание, что все, что делает @abstractmethod, это добавляет декорированный метод к набору, к которому обращается механизм метакласса при попытке создания экземпляра класса. Победить этот механизм тривиально:

>>> Baz.__abstractmethods__
frozenset({'m'})
>>> Baz.__abstractmethods__ = set()
>>> b = Baz()
>>>

)


Обратите внимание, что ABC сам по себе является тривиальным классом, который использует ABCMeta в качестве метакласса, что заставляет его использовать любой из его потомков.

# Docstring omitted; see
# https://github.com/python/cpython/blob/3.7/Lib/abc.py#L166
# for the original
class ABC(metaclass=ABCMeta):
    __slots__ = ()
0 голосов
/ 06 мая 2019

Что сказал Чепнер, а также читабельность. Наследование от ABC дает вашим читателям понять, что вы делаете.

>>> from abc import ABC, abstractmethod
>>> 
>>> class Foo:
...     @abstractmethod
...     def f(self):
...         pass
... 
>>> class Bar(Foo):
...     pass
... 
>>> Bar().f()
>>> 
>>> class Baz(ABC):
...     @abstractmethod
...     def f(self):
...         pass
... 
>>> class Quux(Baz):
...     pass
... 
>>> Quux().f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Quux with abstract methods f
...