【问题标题】:Python: hierarchy of abstract classes without abstract methodsPython:没有抽象方法的抽象类层次结构
【发布时间】:2019-12-13 04:47:53
【问题描述】:

所以,这里有一个问题:

  1. 我想定义一个抽象类,比如说AbstractA,它不需要子类来实现它的任何方法,而是扩展它的功能。就 Java 而言,这将是接口类。

  2. 此外,我希望能够创建一个抽象子类,比如说AbstractB,它具有相同的属性,但某些方法重新定义或扩展了基类方法。

我不想让类 (AbstractA) 抽象,例如through the check of class name in __init__ or __new__,因为这需要抽象子类 (AbstractB) 重新定义该方法及其主要功能,即新实例的构造或初始化。或者打电话给super().__init__(...),我也希望避免这样做(也许我在这里错了)。

所以,我想要这样的东西:

class AbstractA:
    def __init__(self):
        # do initialization stuff

    def very_common_method(self, ...):
        # do very common stuff

class AbstractB(AbstractA):
    # do not duplicate initialization stuff here, inherit instead

    def less_common_method(self, ...):
        # do less common stuff

class AX(AbstractA):
    def specific_method_1(self, ...):

class BX(AbstractB):
    def specific_method_2(self, ...):

# Instantiating AbstractA or AbstractB should result in error.
# Instantiating AX or BX should not.

下面我有一个可能的解决方案。 有什么我忽略的缺点吗?更好的解决方案?

谢谢!

【问题讨论】:

  • class AX(AbstractA): pass 应该合法吗?禁止AbstractA 直接实例化但允许AX 实例化有什么意义?
  • @chepner,公平的问题。我必须接受这个要求更多的是一种偏好而不是必要性。我想要抽象类的原因是因为在语义上实例化这个父类没有意义。就像在实现虚拟动物时,实例化动物或哺乳动物是没有意义的。相反,应该实例化更详细的物种。因此,我想让滥用仅用于定义公共属性的类变得更加困难。至于拳头部分,完全禁止mususe还不够难,但足以防止意外。有点像_private_identifiers。

标签: python inheritance abstract-class


【解决方案1】:

以下是可能的解决方案:

class AbstractA:
    _is_abstract = True

    def __init__(self):
        if self._is_abstract:
            raise RuntimeError("Abstract class instantiation.")
        # do initialization stuff

    def __init_subclass__(self):   # is called every time class is subclassed
        self._is_abstract = False  # thus makes sure abstract check fails on a subclass

class AbstractMixin:
    def __init_subclass__(self):
        self._is_abstract = True

class AbstractB(AbstractMixin, AbstractA):  # AbstractMixin takes precendence on MRO,
    # inherit __init__                      # so the class abstract check returns True.

    def __init_subclass__(self):
        self._is_abstract = False

class A(AbstractA):
    pass

class B(AbstractB):
    pass

AbstractA()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __init__
RuntimeError: Abstract class instantiation.

AbstractB()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __init__
RuntimeError: Abstract class instantiation.

A()
<__main__.A object at 0x7f0bba5112e8>

B()
<__main__.B object at 0x7f0bba511438>

【讨论】:

    【解决方案2】:
    class A(object):
    
        def __init__(self):
            if self.__class__ == A:
                raise RuntimeError("Abstract class instantiation.")
            print(self.__class__.__name__)
    
    
    class B(A):
        pass
    
    
    
    >>> A()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in __init__
    RuntimeError: Abstract class instantiation.
    
    >>> B()
    B
    <__main__.B object at 0x7f8c816a58d0>
    >>> 
    
    

    【讨论】:

    • 我已经特别提到了这种方法以及为什么我不想在问题中使用它。
    【解决方案3】:

    在 Python 中,您可能会将“抽象”基类实现为 mix-ins。你不需要做任何特别的事情;按照惯例,您可以在名称中添加Mixin,以表明它不打算直接实例化,而只是用作其他类的基类。

    class AMixin:
        def __init__(self):
            # do initialization stuff
    
        def very_common_method(self, ...):
            # do very common stuff
    
    class BMixin(AMixin):
        # do not duplicate initialization stuff here, inherit instead
    
        def less_common_method(self, ...):
            # do less common stuff
    
    class AX(AMixin):
        def specific_method_1(self, ...):
    
    class BX(BMixin):
        def specific_method_2(self, ...):
    
    class Foo:
        ...
    
    class Bar(Foo, BMixin):
        ...
    

    【讨论】:

    • 名称中的 Mixin 并不表示它应该只是“用作基类”,而是表示它应该用作混合,即二级、三级等父级,提供一些额外的功能。
    猜你喜欢
    • 1970-01-01
    • 2017-04-03
    • 1970-01-01
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 2017-05-07
    • 2020-03-12
    相关资源
    最近更新 更多