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

5

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

这是我见过的主要方法:

class A(ABC):
    @abstractmethod
    def foo(self):
        pass

然而,这并不会防止您在扩展该类时调用抽象方法。
在Java中,如果您尝试执行类似的操作,会出现错误,但在Python中不会。
class B(A):
    def foo(self):
        super().foo()
B().foo() # does not raise an error

为了复制Java的行为,您可以采用以下方法:
class A(ABC):
    @abstractmethod
    def foo(self):
        raise NotImplementedError

然而,在实际应用中,我很少看到后一种解决方案,即使它显然是最正确的。是否有特定的原因更喜欢第一种方法而不是第二种方法?

1个回答

6
如果你希望在子类中调用超类的抽象方法时出现错误,则需要手动引发异常。(即使它能够直接与类一起工作,也需要创建一个 Exception 类的实例,并在 raise 命令中使用 NotImplementedError()。)
然而,现有的行为实际上非常方便:如果你的抽象方法只包含 pass,则可以有任意数量的子类继承基类,并且只要至少一个实现抽象方法,它就会起作用。即使它们全部调用 super() 等效方法而没有检查其他任何内容。
如果在复杂层次结构中(例如,使用 mixins 等),会调用错误(NotImplementedError 或其他错误),则每次调用 super 时都需要检查是否已引发错误,以便跳过它。值得一提的是,可以使用条件检查 super() 是否会与其中方法是抽象的类相遇。
if not getattr(super().foo, "__isabstractmethod__", False):
     super().foo(...)

既然你希望当你达到方法层次结构的基础时,它什么都不做,那么如果什么也不发生,那就更简单了!

我的意思是,看看这个:

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):
    ...
B.validateC.validate都不需要担心层次结构中的其他类,只需执行它们自己的操作并传递即可。 如果A.validate引发异常,则两个方法都必须在try: ...; except ...: pass语句内执行super().validate(...),或者在奇怪的if块内执行,这样做没有任何好处。 更新 - 我刚在官方文档中找到了这个注释:

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

如果您能在评论中回复,我甚至会向您提出一个个人问题:我知道在Java中这不是很相关,因为不能有多重继承,因此即使在大的继承体系中,第一个子类实现抽象方法通常也会被广泛知晓。但是,在Java项目中,如果可以选择各种基本具体类,并以任意顺序进行其他操作,那么由于抽象方法的提出,该如何解决?


谢谢回复,你的例子非常清晰。基本上,您是说无论抽象还是非抽象类型,子类都应该能够调用父类。对吗?这是您尊重单一职责原则的方式。对吗?如果子类B通过简单调用覆盖了类A的抽象方法,那么这是可以的,因为这意味着类B没有进一步的责任。对吗?我想回答您的问题,但我不理解问题。您所说的“选择一个类”和“继续其他类”是什么意思? - Angelo
“选择一个类”是指从可能存在的抽象类的各种具体实现中选择一个作为继承层次结构中的第一个。而“继续进行其他操作”则是选择其他这样的具体类实现,以继续继承层次结构,直到达到真正使用的实现,即在几个级别之下。 - jsbueno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接