强制使用专门的子类

时间:2018-12-24 16:23:44

标签: python oop polymorphism

当使用某些参数调用超类时,我试图强制使用更专门的类。具体来说,我有一个Monomial类(其__init__接受coef不足和power)和一个Constant类。我希望每当Monomialpower=0调用时,都返回一个Constant实例。

这些类的目的是建立一个用于生成“随机”数学函数的框架。

我最初的经历:

class Monomial(Function):
    def __init__(self, coef: int, power: int, inner=Identity()):
        super().__init__(inner)
        self.pow = power
        self.coef = coef


class Constant(Monomial):
    def __init__(self, c: int):
        super().__init__(c, 0)

我尝试添加以下__new__方法:

class Monomial(Function):
    def __new__(cls, coef: int, power: int, inner=Identity()):
        if power == 0:
            return Constant(coef)
        instance = object.__new__(Monomial)
        instance.__init__(coef, power, inner)
        return instance

问题在于,现在每当创建新的Constant时,就会调用Monomial的{​​{1}}方法(签名不匹配)。

执行此操作的最佳方法是什么?

2 个答案:

答案 0 :(得分:3)

使用factory method方法怎么样?当确切的实例类型应动态定义时,这是一个不错的选择。看起来像:

class Monomial(Function):
    @staticmethod
    def create(coef: int, power: int, inner=Identity()):
        if power == 0:
            return Constant(coef)
        else:
            return Monomial(coef, power)

x = Monomial.create(...)

答案 1 :(得分:1)

调用类时返回不同类类型的方法是重写__new__方法,而不是__init____new__返回的值是用作实例的值(与__init__不同,它甚至不允许返回值)。您已正确启动,但尝试在__new__内部通过调用实例化子类,只会重新输入Monomial.__new__-您可能会在此处遇到递归错误。

因此,即使Python允许更改__new__的返回类型,也许您也应该考虑拥有一个工厂函数(独立于任何类)的想法,该函数将返回适当类的实例。有时“越简单越好”。

无论如何,工厂方法的代码是:

class Monomial(Function):

    def __init__(self, coef: int, power: int, inner=Identity()):
        super().__init__(inner)
        self.pow = power
        self.coef = coef

class Constant(Monomial):
    def __init__(self, c: int):
       super().__init__(self, coef=c, power=0)
       ...

def create_monomial(coef, power):
    if power == 0:
         return Constant(coef)
    return Monomial(coef, power)

(您可能会发现create_monomial也可以是静态或类方法)

而且,如果您真的认为这样会更好,则解开__new__方法的一种方法是:

class Monomial(Function):
    def __new__(cls, coef: int, power: int = 0, inner=Identity()):
        if power == 0:
            cls = Constant
        return super().__new__(cls)

class Constant(Monomial):
    def __init__(self, c: int, **kw):
        super().__init__(c, 0)

如果__init__的返回是__new__的实例,Python的实例化机制将调用self-因此将正确调用Monomial和常量__init__。您只需要修复Constant的__init__就不会破坏它会得到的power = 0的局部参数。

在这里修复签名将花费更多的工作,并且可能涉及使用元类来实际上吞并其常量__call__中未使用的power到Constant的__init__

还请注意,这里的“真正的解决方法”是调用super().__new__需要显式传递该类-与super()的其他用法不同,其中{self}或“ cls”由Python提供。这是由于__new__实际上是一个静态方法-Python在构建类时向其中添加了cls,但是通过“ classmethods”所使用的其他机制。