简体   繁体   English

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

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

I want to create an abstract base class in Python where part of the contract is how instances can be created.我想在 Python 中创建一个抽象基类,其中合同的一部分是如何创建实例。 The different concrete implementations represent various algorithms that can be used interchangeably.不同的具体实现代表了可以互换使用的各种算法。 Below is a simplified example ( usual disclaimer - the real use-case is more complex):下面是一个简化的示例(通常的免责声明- 实际用例更复杂):

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()

The above works, but has the drawback that I can't call super().__init__(...) in ConcreteAlgorithm.__init__ , which might break certain inheritance scenarios, I think (correct me if I'm wrong here, but calling super is important for multiple inheritance, right?).上述方法有效,但有一个缺点是我不能在ConcreteAlgorithm.__init__中调用super().__init__(...) ,这可能会破坏某些继承场景,我认为(如果我在这里错了,请纠正我,但调用super对于多重继承很重要,对吧?)。 (Strictly speaking __init__ can be called, but with the same signature as the subclass __init__ , which doesn't make sense). (严格来说__init__可以被调用,但与子类__init__具有相同的签名,这没有意义)。

Python classes are callables, so I could also express it like this: 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))

This works and doesn't have the drawback mentioned above, but I do like having the __init__ -signature in the abstract base class for documentation purposes.这可行并且没有上面提到的缺点,但我确实喜欢在抽象基类中使用__init__ -signature 以用于文档目的。

Finally, it is possible to have abstract classmethods, so this approach works as well:最后,可以有抽象类方法,所以这种方法也有效:

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))

This works, but I lose the nice property of using algorithm like a callable (it's just more flexible, in case someone actually wants to drop in a function, for example to decide which algorithm to use based on certain parameter values).这行得通,但是我失去了使用诸如可调用algorithm之类的算法的好特性(它更灵活,以防有人真的想加入一个函数,例如根据某些参数值决定使用哪种算法)。

So, is there an approach that satisfies all three requirements:那么,是否有一种方法可以满足所有三个要求:

  1. Full documentation of the interface in the abstract base class.抽象基类中接口的完整文档。
  2. Concrete implementations usable as callables.可用作可调用对象的具体实现。
  3. No unsafe behavior like not being able to call the base-class __init__ .没有不安全的行为,比如不能调用基类__init__

Strictly speaking __init__ can be called, but with the same signature as the subclass __init__ , which doesn't make sense.严格来说__init__可以被调用,但是与子类__init__具有相同的签名,这没有意义。

No, it makes perfect sense.不,这很有意义

You're prescribing the signature because you require each child class to implement it exactly.您正在规定签名,因为您要求每个子类都准确地实现它。 That means you need to call it exactly like that as well.这意味着您也需要完全这样称呼它。 Each child class needs to call its super().__init__ exactly according to the abstract definition, passing all defined parameters along.每个子类都需要完全根据抽象定义调用它的super().__init__ ,并传递所有定义的参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM