带有默认实现的@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 复制了答案,因为我发现没有更好的方法可以用更简单的术语来解释。
我见过声明抽象方法的代码实际上有一个非平凡的主体。
这有什么意义,因为无论如何您都必须具体实施 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 复制了答案,因为我发现没有更好的方法可以用更简单的术语来解释。