在 Python 中实现抽象类的最佳方法

Neo*_*Neo 3 oop abstract-class design-patterns pep8 python-3.x

在 Python 中实现抽象类的最佳方法是什么?

这是我见过的主要方法:

class A(ABC):
    @abstractmethod
    def foo(self):
        pass
Run Code Online (Sandbox Code Playgroud)

但是,它不会阻止您在扩展该类时调用抽象方法。

在 Java 中,如果您尝试执行类似的操作,则会出现错误,但在 Python 中不会:

class B(A):
    def foo(self):
        super().foo()
B().foo() # does not raise an error
Run Code Online (Sandbox Code Playgroud)

为了复制相同的 Java 行为,您可以采用以下方法:

class A(ABC):
    @abstractmethod
    def foo(self):
        raise NotImplementedError
Run Code Online (Sandbox Code Playgroud)

但是,实际上我很少看到后一种解决方案,即使显然是最正确的解决方案。是否有特定的理由更喜欢第一种方法而不是第二种方法?

jsb*_*eno 5

如果您真的希望在子类之一尝试调用超类抽象方法时引发错误,那么,是的,您应该手动引发它。(然后,为 raise 命令创建一个 Exception 类的实例,raise NotImplementedError()即使它直接与该类一起工作)

但是,现有的行为实际上很方便:如果您的抽象方法只包含一个pass,那么您可以有任意数量的子类继承您的基类,只要至少有一个实现了抽象方法,它就可以工作。即使他们都调用了 super() 等效方法,而不检查任何其他内容。

如果NotImplementedError在复杂的层次结构中使用 mixins 等调用错误 -或任何其他错误,则您需要在每次调用时检查super是否引发错误,只是为了跳过它。作为记录,检查是否super()会命中带有条件的方法是抽象的类是可能的,通过这种方式:

if not getattr(super().foo, "__isabstractmethod__", False):
     super().foo(...)
Run Code Online (Sandbox Code Playgroud)

因为如果你到达一个方法的层次结构的基础,你希望它什么都不做,如果什么都不发生,那就简单多了!

我的意思是,检查这个:

class A(abc.ABC):
    @abstractmethod
    def validate(self, **kwargs):
        pass

class B(A):
    def validate(self, *, first_arg_for_B, second_arg_for_B=None, **kwargs):
        super().validate(**kwargs)
        # perform validation:
        ...

class C(A)
    def validate(self, *, first_arg_for_C **kwargs):
        super().validate(**kwargs)
        # perform validation:
        ...

class Final(B, C):
    ...
Run Code Online (Sandbox Code Playgroud)

既不需要B.validate也不C.validate需要担心层次结构中的任何其他类,只需做他们的事情并继续。如果 A.validate 会引发,则两种方法都必须super().validate(...)try: ...;except ...:pass 语句内或在奇怪的if块内执行,以获得......无。

更新- 我刚刚在官方文档中找到了这个注释:

注意与 Java 抽象方法不同,这些抽象方法可能有一个实现。这个实现可以通过覆盖它的类的 super() 机制调用。在使用协作多重继承的框架中,这可以用作超级调用的端点。 https://docs.python.org/3/library/abc.html#abc.abstractmethod

如果您可以在评论中回复,我什至会回复您一个个人问题:我知道它在 Java 中的相关性要低得多,因为不能具有多重继承,因此,即使在大层次结构中,第一个实现抽象的子类方法通常是众所周知的。但除此之外,在 Java 项目中,人们可以选择各种 Base 具体类中的一个,并以任意顺序处理其他类,因为抽象方法提出,这是如何解决的?