重写__add__方法后出现TypeError

37 投票
5 回答
42151 浏览
提问于 2025-04-16 12:19

我正在尝试理解 __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 个回答

1

我不赞成在使用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)
17
>>> 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 中的第一个值和默认的零相加时)。

93

你需要定义 __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__ 的地方。作为一个有趣的练习,你可以检查一下确实是这样!

撰写回答