Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
543 views
in Technique[技术] by (71.8m points)

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

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'
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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 = 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.

Note that it's like total = total + x and not total += x, because these can have different meanings for classes like list. 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...