簡體   English   中英

如何使用抽象的 __init__ 方法創建一個類?

[英]How to create a class with an abstract `__init__` method?

我想在 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()

上述方法有效,但有一個缺點是我不能在ConcreteAlgorithm.__init__中調用super().__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__ -signature 以用於文檔目的。

最后,可以有抽象類方法,所以這種方法也有效:

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__ ,並傳遞所有定義的參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM