【问题标题】:When should one inherit from ABC?什么时候应该从ABC继承?
【发布时间】:2019-09-24 07:18:48
【问题描述】:

当不想实例化类时,我一直认为应该从abc.ABC 继承。但我刚刚意识到,如果一个类有一个@abstractmethod,那么也不能实例化它。

还有其他理由从ABC继承吗?

【问题讨论】:

    标签: python abc


    【解决方案1】:

    除非您使用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__ = ()
    

    【讨论】:

    • 好吧,__slots__ 不会让 类对象变小,这样你继承的类就可以使用__slots__
    • 好点,虽然没有什么能阻止一个类使用__slots__,即使它的父类不使用它。我想我错过了空 __slots__ 属性的意义。
    • 如果 any 父级没有 __slots__ 则无论您是否定义 __slots__ 您的 sublcas 仍将有一个带有相应字典的 __dict__ 属性命名空间
    • "请注意,ABC 本身是一个普通类,它使用 ABCMeta 作为其元类,这使得它的任何后代也使用它。"是的,但并非总是如此!请注意元类冲突,正如官方标准中提到的那样:Note that the type of ABC is still ABCMeta, therefore inheriting from ABC requires the usual precautions regarding metaclass usage, as multiple inheritance may lead to metaclass conflicts. from docs.python.org/3/library/abc.html>
    • 我的意思是ABC 没有做任何事情除了 实例化ABCMeta
    【解决方案2】:

    chepner 所说的以及可读性。继承 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-13
      • 2017-09-29
      • 1970-01-01
      • 2013-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多