【问题标题】:Class that should not be instantiated不应实例化的类
【发布时间】:2017-08-07 19:56:05
【问题描述】:

我想创建一个类层次结构,其中我有一个可以自行实例化的类Block。然后我有一个类List,它继承自Block,并包含所有列表共有的方法,最后我有类OrderedListLableledList 等继承自List。我希望人们能够实例化OrderedList 等,但不能实例化List

换句话说,你可以实例化一个普通的Block,你可以实例化一个继承自ListOrderedList,它继承自Block,但你不能实例化List

所有对谷歌的尝试都导致了抽象基类,但没有提供适合这种情况的示例,我无法推断。

【问题讨论】:

  • 您在类声明中指定class List(metaclass=ABCMeta):

标签: python python-3.x class inheritance


【解决方案1】:

从位于abc 模块中的ABC 继承并生成在基类中实现的方法(继承自List@abstractmethods(位于abc 中的装饰器):

from abc import ABC, abstractmethod

class List(ABC, Block):

    @abstractmethod
    def size(self):
        return 0 

定义了 @abstractmethods 的 ABC 禁止实例化。

【讨论】:

  • 不,我的列表与 python 列表完全无关(它与文本列表有关)。我需要 List 从Block 继承,并且我需要List 中的几个方法不是抽象的,以便它们可以被OrderedList 继承。这是我无法弄清楚的事情的组合。
  • @MarkBaker 您不需要将 所有方法 抽象化;只是那些你需要重新实现的。您可以在List 中使用“通用”方法,并在任何子类中毫无问题地使用它们。不过,List 中定义为 abstract 的所有方法都需要在子类中实现。
【解决方案2】:

您的 List 类应该有 ABCMeta 作为元类,并使 init 方法抽象。

from abc import ABCMeta
class List(metaclass=ABCMeta):
    @abstractmethod
    __init__():
        pass

https://docs.python.org/3/library/abc.html

【讨论】:

    【解决方案3】:

    做到这一点的“一个显而易见”的方法是使用 ABCMeta 并将一些方法标记为抽象的,如其他答案中记录的那样。

    但是,如果在您的情况下,您没有一组必须以强制方式覆盖的方法(假设您的 __init__ 在某些情况下是可重用的,其他列表方法也是如此):

    在这种情况下,您可以创建一个__new__ 方法来检查被实例化的类是否是自己的类,并引发。要做到这一点,您必须使用仅在 Python 文档的角落中作为 documentend 的魔法 __class__ 变量 - 如果您在任何方法主体中尽可能多地使用 __class__ 变量,它将自动获取类的值,其中它是在运行时声明的。它是 Python 3 的无参数 super 机制的一部分。

    因此:

    class List(Block):
        def __new__(cls, *args, **kw):
            if cls is __class__:
                raise TypeError(cls.__name__ + " can't be directly instantiated")
            return super().__new__(cls, *args, **kw)
    

    顺便说一句,如果您的模式允许,您应该优先使用 ABCMeta 抽象方法。请注意,如果您的类使用自定义元类,它也会与 ABCMeta 冲突 - 因此您可能也需要使用此方法

    (如果你不进一步自定义__new__,那么你最好不要在__new__方法上传递argskw上游:如果__init__被定义,Python的object.__new__会忽略额外的参数但__new__ 不在子类中 - 但如果两者都被定义,则会引发错误)

    【讨论】:

      【解决方案4】:

      以下与口译员的对话应说明这是如何实现的。用Block继承抽象基类后,只需将List上的初始化器标记为abstractmethod。这将阻止类的实例化,而不会导致子类出现问题。

      >>> import abc
      >>> class Block(abc.ABC):
          def __init__(self, data):
              self.data = data
      
      
      >>> class List(Block):
          @abc.abstractmethod
          def __init__(self, data, extra):
              super().__init__(data)
              self.extra = extra
      
      
      >>> class OrderedList(List):
          def __init__(self, data, extra, final):
              super().__init__(data, extra)
              self.final = final
      
      
      >>> instance = Block(None)
      >>> instance = List(None, None)
      Traceback (most recent call last):
        File "<pyshell#42>", line 1, in <module>
          instance = List(None, None)
      TypeError: Can't instantiate abstract class List with abstract methods __init__
      >>> instance = OrderedList(None, None, None)
      >>> 
      

      【讨论】:

      • 啊,说得太清楚了!所以 Block 可以继承 ABC 并且仍然被实例化。它将init 标记为@abstractmethod,这使得无法实例化该类。所以不需要多重继承。
      猜你喜欢
      • 2020-03-23
      • 2011-09-09
      • 1970-01-01
      • 1970-01-01
      • 2014-04-03
      • 1970-01-01
      • 1970-01-01
      • 2014-06-18
      • 1970-01-01
      相关资源
      最近更新 更多