【问题标题】:How to inherit from ABC and another class如何从 ABC 和另一个类继承
【发布时间】:2021-03-12 19:20:16
【问题描述】:

我有课

class A(ABC):
  @abstractmethod
  def m():
    pass

还有一堆继承自它的类。

class M1(A):
  ...

class M2(A):
  ...

...

其中一些有很多共同点,因为它们共享很多功能,因此我想介绍一个公共基类,它本身继承自 A,但它没有引入任何新的 @abstractmethod 方法:


       A
       |
  +----+----+
  |    |    |
  M1   M2   B
            |
         +--+--+
         |     |
         K1   K2

class B 的定义应该是什么样的?

我使用的是 Python 3.8

编辑:我特意问的是 PyCharm 告诉我“B 必须继承所有抽象方法”

【问题讨论】:

    标签: python oop inheritance python-3.8


    【解决方案1】:

    你可以简单地使用

    class B(A):
       pass
    

    或任何其他所有孩子通用的代码,但没有m。由于没有m,这仍然是抽象的。


    另一种方法是使用

    class B(A, ABC):
       pass
    

    在我看来,这不太好。这表明B 既是A 又是ABC,而前一个替代项指出BA,并且它是ABC,只要A 是@ 987654333@自己。

    考虑您稍后决定A 不是ABC 的情况。对于M1M2,无需进行任何更改。对于B,您现在还需要将其删除为ABC。虽然确保AB 在子类化ABC 中同步并不是代码维护方面的巨大开销,但我认为它确实表明设计存在问题,尽管PyCharm 警告前一种替代方案(我会将其抑制为误报)。

    【讨论】:

    • 也许我只是被 PyCharm 宠坏了——它抱怨 B“必须继承所有抽象方法”所以我想我应该更好地指出这个类也是一个 ABC .我应该忽略这个警告吗?
    • @scravy 我想是的。它已在 PyCharm 中作为误报提交:youtrack.jetbrains.com/issue/PY-16132
    • 在引用的问题中,抽象的子类 Mixin 被明确标记为此类(以 python 2.7 方式)。我的课没有以任何方式标记。我知道这个类是有效地抽象的,它的行为是这样的(例如不能被实例化),但它没有被标记为这样。在其他语言中,我必须明确地将其标记为抽象并查看它,我确实认为最好表明这个类的意图实际上是抽象的。让我重新表述我的问题:如果除了A 我还要混入ABC 会不会很痛? class B(A, ABC)
    • @scravy 我更新了答案以解决您评论末尾的问题,并解释了为什么我认为我链接的 PyCharm 问题具有相似的基本原理。
    • @scravy:忽略它。如果可能,禁用警告。从ABC 继承甚至不会使类抽象,并且您将ABC 粘贴到继承图中的位置越多,您遇到MRO 问题的可能性就越大。 (我个人完全避免使用 ABC 并使用 ABCMeta 元类,它可以真正发挥作用 - ABC 只是一种触发 ABCMeta 元类的方式,以供对元类不太熟悉的人使用。)
    【解决方案2】:

    在您的特定情况下,您不需要混合任何东西。我个人不使用 Pycharm,但是将 B 创建为 A 的子类就可以了,python 不会抱怨。

    >>> from abc import ABC, abstractmethod
    >>> class A(ABC):
    ...     @abstractmethod
    ...     def m(self):
    ...         pass
    ... 
    >>> class B(A):
    ...     def common_method(self) -> str:
    ...         return 'b'
    ... 
    >>> class K1(B):
    ...     def m(self) -> str:
    ...         return 'k1'
    ... 
    >>> class K2(B):
    ...     def m(self) -> str:
    ...         return 'k2'
    ... 
    >>> k1 = K1()
    >>> k2 = K2()
    >>> k1.m()
    'k1'
    >>> k2.m()
    'k2'
    >>> k1.common_method()
    'b'
    >>> k2.common_method()
    'b'
    >>> # However you can't do this
    >>> b = B()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: Can't instantiate abstract class B with abstract methods m
    

    如果您有不同但相似的结构(不是直接的问题,但仍然相关)

           A        <----- this is not abstract
           |
      +----+----+
      |    |    |
      M1   M2   B   <------ you want this to be abstract and also group some
                |           functionality for K1 and K2
             +--+--+
             |     |
             K1   K2
    

    您可以通过定义metaclass 而不是将ABCA 子类化为mixin 来实现,而无需混合类(无论如何,元类完成所有工作)。这是你可以做到的。

    >>> from abc import ABCMeta, abstractmethod
    >>> class A:
    ...     def m(self) -> str:
    ...         return 'a'
    ... 
    >>> class B(A, metaclass=ABCMeta):
    ...     @abstractmethod
    ...     def n(self):
    ...         pass
    ...     def common_method(self) -> str:
    ...         return 'b'
    ... 
    >>> class K1(B):
    ...     def n(self) -> str:
    ...         return 'k1'
    ... 
    >>> class K2(B):
    ...     def n(self) -> str:
    ...         return 'k2'
    ... 
    >>> k1 = K1()
    >>> k2 = K2()
    >>> k1.m()
    'a'
    >>> k2.m()
    'a'
    >>> k1.n()
    'k1'
    >>> k2.n()
    'k2'
    >>> k1.common_method()
    'b'
    >>> k2.common_method()
    'b'
    >>>
    

    【讨论】:

      猜你喜欢
      • 2019-03-31
      • 2014-11-22
      • 1970-01-01
      • 2020-12-16
      • 2021-09-19
      • 1970-01-01
      • 1970-01-01
      • 2014-05-22
      • 1970-01-01
      相关资源
      最近更新 更多