【问题标题】:Best way to implement abstract classes in Python在 Python 中实现抽象类的最佳方法
【发布时间】:2020-06-28 18:10:03
【问题描述】:

在 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

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

【问题讨论】:

    标签: python-3.x oop design-patterns abstract-class pep8


    【解决方案1】:

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

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

    如果错误 - NotImplementedError 或任何其他错误将在复杂的层次结构中被调用,使用 mixins 等,您需要在每次调用 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 语句内或在奇怪的 if 块内执行 super().validate(...),以获得......什么都没有。

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

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

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

    【讨论】:

    • 感谢您的回复:您的示例非常清楚。基本上,您是说子类应该始终能够调用父类,无论是否抽象类型。正确的?这是您尊重单一责任原则的一种方式。正确的?如果一个子类:B,通过简单地调用它来覆盖类:A的抽象方法,那很好,因为这意味着B类没有责任做进一步的事情。正确的?我想回答你的问题,但我不明白这个问题。 “选择一门课”和“继续学习其他课”是什么意思?
    • “选择一个类”是:选择一个抽象类的可能各种具体实现中的一个作为继承层次结构中的第一个。并且“与其他人一起进行”是采用其他此类具体的类实现来继续继承层次结构,直到到达将真正使用的实现,下面的某些级别。
    猜你喜欢
    • 2020-09-21
    • 2010-10-29
    • 2011-12-01
    • 1970-01-01
    • 2016-12-28
    • 2016-11-08
    • 2013-05-30
    • 2016-08-13
    • 1970-01-01
    相关资源
    最近更新 更多