如何用抽象的' __init__ '方法创建一个类?



我想在Python中创建一个抽象基类,其中合同的一部分是如何创建实例。不同的具体实现代表了可以互换使用的各种算法。下面是一个简化的示例(通常的免责声明-实际用例更复杂):

from abc import ABC, abstractmethod
from typing import Type

class AbstractAlgorithm(ABC):
@abstractmethod
def __init__(self, param: int):
pass
@abstractmethod
def get_result(self) -> int:
pass

class ConcreteAlgorithm(AbstractAlgorithm):
def __init__(self, param: int):
self._param = param
def get_result(self) -> int:
return self._param * 2

def use_algorithm(algorithm: Type[AbstractAlgorithm]) -> int:
a = algorithm(10)
return a.get_result()

上面的工作,但有缺点,我不能调用super().__init__(...)ConcreteAlgorithm.__init__,这可能会破坏某些继承场景,我认为(纠正我,如果我错了,但调用super是重要的多重继承,对吗?)。(严格来说,__init__可以调用,但是与子类__init__具有相同的签名,这没有意义)。

Python类是可调用的,所以我也可以这样表示:

from abc import ABC, abstractmethod
from typing import Callable

class AbstractAlgorithm(ABC):
@abstractmethod
def get_result(self) -> int:
pass

class ConcreteAlgorithm(AbstractAlgorithm):
def __init__(self, param: int):
self._param = param
def get_result(self) -> int:
return self._param * 2

def use_algorithm(algorithm: Callable[[int], AbstractAlgorithm]) -> int:
a = algorithm(10)
return a.get_result()

print(use_algorithm(ConcreteAlgorithm))

这可以工作,并且没有上面提到的缺点,但是我确实喜欢在抽象基类中使用__init__签名,以用于文档目的。

最后,可以有抽象类方法,所以这种方法也可以工作:

from abc import ABC, abstractmethod
from typing import Type

class AbstractAlgorithm(ABC):
@classmethod
@abstractmethod
def initialize(cls, param: int) -> "AbstractAlgorithm":
pass
@abstractmethod
def get_result(self) -> int:
pass

class ConcreteAlgorithm(AbstractAlgorithm):
@classmethod
def initialize(cls, param: int) -> "ConcreteAlgorithm":
return cls(param)
def __init__(self, param: int):
self._param = param
def get_result(self) -> int:
return self._param * 2

def use_algorithm(algorithm: Type[AbstractAlgorithm]) -> int:
a = algorithm.initialize(10)
return a.get_result()

print(use_algorithm(ConcreteAlgorithm))

这是有效的,但我失去了使用algorithm像一个可调用的好属性(它只是更灵活,如果有人真的想在一个函数中,例如,决定基于某些参数值使用哪个算法)。

那么,有没有一种方法可以满足这三个要求:

  1. 抽象基类中接口的完整文档。
  2. 可调用的具体实现。
  3. 没有不安全的行为,比如不能调用基类__init__

严格来说可以调用__init__,但是与子类__init__具有相同的签名,这没有意义。

不,它使完美有意义。

你规定了签名,因为你要求每个子类都精确地实现它。这意味着你也需要像那样调用。每个子类都需要根据抽象定义精确地调用其super().__init__,并传递所有已定义的参数。

最新更新