Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing __add__ magic method to allow `sum` to operate on objects

Tags:

python

I'm defining a class Foo with an __add__ method:

class Foo():
    def __init__(self, data):
        self.data = data

    def __add__(self, other):
        return self.data + other.data

The data attribute is to hold strings only.

Ultimate goal is to pass a list/iterable of Foo objects to a sum method to concatenate strings:

In [580]: s = [Foo('foo'), Foo('bar'), Foo('baz')] 

In [581]: sum(s)

Expected:

foobarbaz

Actual:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-581-4c1cdc765951> in <module>()
----> 1 sum(s)

TypeError: unsupported operand type(s) for +: 'int' and 'Foo'

I thought sum calls object.__add__ to sum the two methods together. I know I can solve this another way (possibly with functools.reduce) but is there a way I can get this to work with sum?

Edit: Forgot to mention, tried this:

In [582]: sum(s, Foo(''))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-582-e3eeba9a6d93> in <module>()
----> 1 sum(s, Foo(''))

TypeError: Can't convert 'Foo' object to str implicitly
like image 407
cs95 Avatar asked Oct 19 '25 13:10

cs95


2 Answers

You need to pass an initial value to sum. Since your class only supports adding to other instances of the same class, you'd have to specify an "empty" instance as the initial value:

sum(s, Foo(''))

However, your class also has a problem: it only allows adding Foo instances to other Foo instances, but the addition returns a string, not a Foo instance. So you can't add more than two Foos, even "manually":

>>> Foo('a') + Foo('b') + Foo('c')
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    Foo('a') + Foo('b') + Foo('c')
TypeError: must be str, not Foo

To fix this, you need to make your __add__ return a Foo:

def __add__(self, other):
    return Foo(self.data + other.data)

Then when you do sum(s, Foo('')), what you will get is not the string foobarbaz, but a Foo instance whose .data attribute is 'foobarbaz'.

like image 91
BrenBarn Avatar answered Oct 22 '25 03:10

BrenBarn


__add__ must return Foo(self.data + other.data).

As a rule of thumb, the __methods__ should return objects of type type(self), although this is not always true. E.g. what would you expect Int + Int or Float + Float to return?

like image 34
ForceBru Avatar answered Oct 22 '25 03:10

ForceBru



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!