Walk MRO for Python返回NotImplemented的特殊方法

时间:2017-12-14 02:17:45

标签: python oop multiple-inheritance method-resolution-order

我有一个代数对象的类层次结构,它实现了__mul____add__之类的特殊方法,并使用了多重继承。我以某种方式假设Python(> = 3.5)会走方法解析顺序(mro)来找到第一个不返回NotImplemented的方法。唉,情况似乎并非如此。请考虑以下最小示例:

class A():
    def __mul__(self, other):
        return "A * %s" % other

class B():
    def __mul__(self, other):
        if isinstance(other, int):
            return "B * %s" % other
        else:
            return NotImplemented

class C(B, A):
    pass

class D(B, A):
    def __mul__(self, other):
        res = B.__mul__(self, other)
        if res is NotImplemented:
            res = A.__mul__(self, other)
        return res

在此代码中,我实现了D所需的行为:

>>> d = D()
>>> d * 1
'B * 1'
>>> d * "x"
'A * x'

但是,实际上我希望C的行为与D相同,但不是>>> c = C() >>> c * 1 'B * 1' >>> c * "x" Traceback (most recent call last): File "<ipython-input-23-549ffa5b5ffb>", line 1, in <module> c * "x" TypeError: can't multiply sequence by non-int of type 'C'

NotImplemented

我理解发生了什么,当然:我只是在mro中返回第一个匹配方法的结果(我只希望将D.__mul__作为特殊值处理)

我的问题是,是否有任何方法可以编写类似int x ; if(x>5) { // you're checking if x is bigger than 5 // If true print it's bigger... System.out.print("It's bigger than five"); }else{ // anything else (if not bigger) print it's not bigger... System.out.print("It's not bigger than five"); } 的样板代码(对于所有类的所有数值特殊方法,它们基本相同)。我想我可以写一个类装饰器或元类来自动生成所有这些方法,但我希望有一些更简单的(标准库)方式,或者有人已经做过这样的事情。

1 个答案:

答案 0 :(得分:1)

当你提出要求时,Python会走向MRO,并不会暗示继续检查更高。更改代码以使用super()的合作继承(将MRO引导到下一个类的请求),否则返回NotImplemented并且它应该有效。它根本不需要CD定义__mul__,因为它们不会为其功能添加任何内容:

class A():
    def __mul__(self, other):
        return "A * %s" % other

class B():
    def __mul__(self, other):
        if isinstance(other, int):
            return "B * %s" % other
        try:
            return super().__mul__(other)  # Delegate to next class in MRO
        except AttributeError:
            return NotImplemented  # If no other class to delegate to, NotImplemented

class C(B, A):
    pass

class D(B, A):
    pass  # Look ma, no __mul__!

然后测试:

>>> d = D()
>>> d * 1
'B * 1'
>>> d * 'x'
'A * x'

super()的神奇之处在于它甚至可以在多个继承场景中运行,其中一个类B在这种情况下对A一无所知,但仍然会愉快地委托给它(或者任何其他可用的课程)如果孩子碰巧从两者继承。如果不是,我们会像之前一样处理结果AttributeError以生成结果NotImplemented,因此像这样的内容按预期工作(它会尝试str&#39; s __rmul__,它不识别非int并爆炸):

>>> class E(B): pass
>>> e = E()
>>> e * 1
'B * 1'
>>> e * 'x'
Traceback (most recent call last)
...
TypeError: can't multiply sequence by non-int of type 'E'