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)