TypeError после переопределения метода __add__
Я пытаюсь понять, как работает __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() работать?
UPDATE
Моя проблема заключается в том, как использовать сумму в списке объектов, поддерживающих __add__
, необходимо сохранить ее как можно более общую.
Ответы
Ответ 1
Вам нужно определить __radd__
, чтобы заставить это работать.
__radd__
- обратное добавление. Когда Python пытается оценить x + y
, он сначала пытается вызвать x.__add__(y)
. Если это не удается, оно возвращается к y.__radd__(x)
.
Это позволяет вам переопределить дополнение, только касаясь одного класса. Рассмотрим, например, как Python должен был бы оценить 0 + x
. Выполняется вызов 0.__add__(x)
, но int
ничего не знает о вашем классе. Вы не можете очень хорошо изменить метод __add__
в int
, следовательно, потребуется __radd__
. Я полагаю, что это форма инверсии зависимостей.
Как указал Стивен, sum
работает на месте, но начинается с 0. Таким образом, самое первое дополнение - это единственное, что нужно использовать __radd__
. Как хорошее упражнение, вы можете проверить, что это так!
Ответ 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
к начальному значению по умолчанию, равному нулю).
Ответ 3
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return self.num += other.num
def __str__(self):
return str(self.num)
one = MyNum(1)
two = MyNum(2)
one + two
print(two.num)
Ответ 4
Еще одна опция - уменьшить (functools.reduce в Python 3.x).
from functools import reduce
from operators import add
d=[MyNum(i) for i in range(10)]
my_sum = reduce(add,d)
Ответ 5
Я выступаю против ретрансляции на сумму() с начальной точкой, отверстие в петле, открытое ниже,
In [51]: x = sum(d, MyNum(2))
In [52]: x.num
Out[52]: 47
Интересно, почему вы получили 47, пока вы ожидаете
... начинаем с 2-го из MyNum(), оставляя сначала и добавляем их до конца, поэтому ожидаемый результат = 44 (сумма (диапазон (2,10))
Правда здесь в том, что 2 не сохраняется как начальный объект/позиция, а рассматривается как дополнение к результату
sum (диапазон (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)