Why a class that can be added to itself can't be summed? (a+a works, sum([a,a]) fails)

Jano :

In the following snippet, I create a Counter object and I try add it to itself using three ways. Standard addition using '+', reducing from a list (internally does the same as previous) and using the function sum() from a list.

The first 2 work and return the expected result, but the third raises a TypeError exception.

From here I have two questions:

  1. What's the reason for the sum() failing if the addition works?
  2. When I add to objects with +, the __ add__ method of the class is used. What method, if any, is used when calling sum()?

Counter is only an example, I'm interested on the general case rather than this one in particular for when I create new classes.

from collections import Counter
from functools import reduce
a = Counter([1,1,1,1])

print(a+a+a)
print(reduce(lambda x, y: x + y, [a,a, a]))
sum([a, a, a])

prints:

Counter({1: 12})
Counter({1: 12})
Traceback (most recent call last):
  File "/home/user/.PyCharmCE2019.3/config/scratches/scratch.py", line 20, in <module>
    print(sum([a, a,]))
TypeError: unsupported operand type(s) for +: 'int' and 'Counter'
kaya3 :

This is because sum([a, a]) is not doing a + a; it is actually doing 0 + a + a. The Counter class instances can be added to instances of Counter, but they cannot be added to the integer 0.

Think about how you'd write the sum function yourself:

def sum(lst):
    total = 0
    for x in lst:
        total += x
    return total

In principle you could initialise total = lst[0] and then iterate over lst[1:] to add the remaining values. The problem with that is, then you can't get the sum of an empty sequence. So Python's actual sum function is like the code above, except you can specify the starting value (docs link), so it's actually more like this:

def sum(lst, start=0):
    total = start
    for x in lst:
        total += x
    return total

That means that for the behaviour you want, you can do sum([a, a], Counter()), because an "empty" Counter serves the same role as 0 does for adding numbers.

In the general case, your class needs an additive identity to use as the starting value for sum; if there is no suitable value for the "identity", you should use reduce instead.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=19422&siteId=1