重写__add__方法后出现TypeError
我正在尝试理解 __add__
是怎么工作的:
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __str__(self):
return str(self.num)
如果我把它们放在一个列表里
d=[MyNum(i) for i in range(10)]
这样是可以的
t=MyNum(0)
for n in d:
t=t+n
print t
但是这样就不行:
print sum(d)
TypeError: unsupported operand type(s) for +: 'int' and 'instance'
我哪里做错了?我该怎么让 sum() 函数正常工作?
我的问题是如何在一个支持 __add__
的对象列表上使用求和,尽量保持通用性。
5 个回答
我不赞成在使用sum()函数时指定起始点,下面的漏洞就暴露了这个问题。
In [51]: x = sum(d, MyNum(2))
In [52]: x.num
Out[52]: 47
你可能会想,为什么你得到了47,而你期待的结果是从MyNum()的第二个数开始,加上第一个数,直到最后,所以你期望的结果是44(也就是sum(range(2,10)))。
其实这里的真相是,2并不是作为起始对象或位置来处理的,而是被当作结果的一部分来加上去的。
sum(range(10)) + 2
哎呀,链接坏掉了!!!!
使用 __radd__
下面是正确的代码。同时注意以下几点:
Python只有在+号右边的对象是你的类实例时,才会调用 __radd__
,比如:2 + obj1。
#!/usr/bin/env python
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __radd__(self,other):
return MyNum(self.num+other)
def __str__(self):
return str(self.num)
d=[MyNum(i) for i in range(10)]
print sum(d) ## Prints 45
d=[MyNum(i) for i in range(2, 10)]
print sum(d) ## Prints 44
print sum(d,MyNum(2)) ## Prints 46 - adding 2 to the last value (44+2)
>>> help(sum)
Help on built-in function sum in module __builtin__:
sum(...)
sum(sequence[, start]) -> value
Returns the sum of a sequence of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0). When the sequence is
empty, returns start.
换句话说,提供一个起始值:
sum(d, MyNum(0))
编辑:我从下面的评论中粘贴过来:
sum
函数默认的起始值是整数零。你写的 MyNum
类不知道怎么把自己和整数相加。要解决这个问题,你有两个选择。你可以给 sum
提供一个和你的类相同类型的起始值,或者你可以实现 __radd__
方法,这个方法是 Python 在处理不同类型的值相加时调用的(比如当 d
中的第一个值和默认的零相加时)。
你需要定义 __radd__
才能让这个功能正常工作。
__radd__
是指 反向加法。当 Python 尝试计算 x + y
时,它首先会尝试调用 x.__add__(y)
。如果这个调用失败了,Python 就会转而调用 y.__radd__(x)
。
这样做的好处是,你只需要修改一个类就能改变加法的行为。比如说,考虑一下 Python 如何计算 0 + x
。它会先尝试调用 0.__add__(x)
,但 int
类型并不知道你的类是什么。因此,你不能直接修改 int
中的 __add__
方法,这就是为什么需要 __radd__
的原因。我想这算是一种依赖倒置的方式。
正如 Steven 所提到的,sum
是就地操作的,但它是从 0 开始的。所以,第一次加法是唯一需要使用 __radd__
的地方。作为一个有趣的练习,你可以检查一下确实是这样!