我有一个代数对象的类层次结构,它实现了__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");
}
的样板代码(对于所有类的所有数值特殊方法,它们基本相同)。我想我可以写一个类装饰器或元类来自动生成所有这些方法,但我希望有一些更简单的(标准库)方式,或者有人已经做过这样的事情。
答案 0 :(得分:1)
当你提出要求时,Python会走向MRO,并不会暗示继续检查更高。更改代码以使用super()
的合作继承(将MRO引导到下一个类的请求),否则返回NotImplemented
并且它应该有效。它根本不需要C
或D
定义__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'