带有默认实现的@abstractmethod 有什么意义?

What is the point of an @abstractmethod with default implementation?

我见过声明抽象方法的代码实际上有一个非平凡的主体。

这有什么意义,因为无论如何您都必须具体实施 class?

难道只是为了让你做这样的事吗?

   def method_a(self):
          super(self).method_a()

我以前在可能有具体实现的情况下使用过它,但我想强制 subclass 实现者考虑该实现是否适合他们。

一个具体的例子:我正在用一个抽象工厂方法实现一个抽象基础class,所以子classes可以定义他们自己的__init__函数,但有一个共同的接口来创建他们。有点像

class Foo(ABC):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    @abstractmethod
    def from_args(cls, a, b, c) -> "Foo":
        return cls(a, b, c)

Subclasses 通常只需要参数的一个子集。在测试时,访问实际使用此工厂方法的框架很麻烦,因此很可能有人会忘记实现 from_args 工厂函数,因为它不会出现在他们通常的测试中。将其设置为 abstractmethod 将无法在不首先实现它的情况下初始化 class,并且在正常测试期间肯定会出现。

简单来说就是强制子classes实现方法,否则class不会初始化

从长远来看,

抽象 classes 是包含一个或多个抽象方法的 classes。抽象方法是声明但不包含实现的方法。抽象classes不能实例化,需要子classes提供抽象方法的实现。

您可以在以下示例中看到这一点:

class AbstractClass:
    
    def do_something(self):
        pass
    
    
class B(AbstractClass):
    pass

a = AbstractClass()
b = B()

如果我们启动这个程序,我们会发现这不是一个抽象 class,因为:

我们可以实例化一个实例,我们不需要在 B 的 class 定义中实现 do_something 我们的示例实现了一个与抽象无关的简单继承案例 class。事实上,Python 本身并不提供抽象的 classes。然而,Python 附带一个模块,该模块提供用于定义抽象基础 类 (ABC) 的基础结构。这个模块被称为 - 出于显而易见的原因 - abc.

以下 Python 代码使用 abc 模块并定义了一个抽象基础 class:

from abc import ABC, abstractmethod
 
class AbstractClassExample(ABC):
 
    def __init__(self, value):
        self.value = value
        super().__init__()
    
    @abstractmethod
    def do_something(self):
        pass

我们现在将使用之前定义的抽象class 定义一个子class。你会注意到我们还没有实现 do_something 方法,尽管我们需要实现它,因为这个方法被装饰器 abstractmethod 装饰为抽象方法。我们得到一个 DoAdd42 无法实例化的异常:

class DoAdd42(AbstractClassExample):
    pass

x = DoAdd42(4)
OUTPUT:
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-2bcc42ab0b46> in <module>
      2     pass
      3 
----> 4 x = DoAdd42(4)
TypeError: Can't instantiate abstract class DoAdd42 with abstract methods do_something

我们将在下面的示例中以正确的方式进行操作,其中我们定义了两个 classes 继承自我们的抽象 class:

class DoAdd42(AbstractClassExample):

    def do_something(self):
        return self.value + 42
    
class DoMul42(AbstractClassExample):
   
    def do_something(self):
        return self.value * 42
    
x = DoAdd42(10)
y = DoMul42(10)

print(x.do_something())
print(y.do_something())
OUTPUT:
52
420

派生自抽象 class 的 class 无法实例化,除非其所有抽象方法都被覆盖。

你可能认为抽象方法不能在抽象基中实现class。这种印象是错误的:一个抽象方法可以在抽象class中有一个实现!即使实现了,subclasses 的设计者也将被迫覆盖实现。与“正常”继承的其他情况一样,可以使用 super() 调用机制调用抽象方法。这使得能够在抽象方法中提供一些基本功能,可以通过 subclass 实现来丰富这些功能。

from abc import ABC, abstractmethod
 
class AbstractClassExample(ABC):
    
    @abstractmethod
    def do_something(self):
        print("Some implementation!")
        
class AnotherSubclass(AbstractClassExample):

    def do_something(self):
        super().do_something()
        print("The enrichment from AnotherSubclass")
        
x = AnotherSubclass()
x.do_something()
        
OUTPUT:
Some implementation!
The enrichment from AnotherSubclass

PS。可耻地从 the-abc-of-abstract-base-classes 复制了答案,因为我发现没有更好的方法可以用更简单的术语来解释。