问题
Today, I automatically wrote some thing like this:
class Foo():
def __init__(self, x):
self.x = x
s = [Foo(1), Foo(2), Foo(3)]
sum_x = sum(s, key = lambda foo: foo.x)
And got this:
TypeError: sum() takes no keyword arguments
Is there any special reason for sum() does not have a key arg?
回答1:
Because you can just write sum(foo.x for foo in s) instead. If you tried to do this with one of the functions that does take a key argument (sorted, min, max, etc.), the function would end up returning the key(s) rather than the original item(s), and getting the original items while sorting by keys is tricky enough that Python gives you a built-in way to do that via a keyword argument.
Thus: There's no special reason for sum to not take a key; rather, those other functions have special reasons why they do take a key. key is the exception, not the rule.
回答2:
There is no key argument because sum() doesn't return the original elements (like sorted(), min() and max() do). Instead, it just sums the inputs.
If, say, min() did not take a key argument, it could not return the minimum Foo() object based on an attribute; it could only return the value of that attribute. But sum() doesn't work that way, it doesn't need to preserve the original objects.
You can easily transform the inputs in a generator expression:
sum(item.x for item in s)
回答3:
Although there is no key param, the good news is that You can use sum
with your Foo object! Others have already pointed out that the easiest way to
do this
sum(item.x for item in s)
However, it's also possible to use it without the comprehension.
Object addition
In order for sum to work, basic addition needs to work first.
In [2]: class Foo:
...: def __init__(self, x):
...: self.x = x
...:
In [3]: Foo(3) + Foo(5)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-f0e9c3a4abb9> in <module>()
----> 1 Foo(3) + Foo(5)
TypeError: unsupported operand type(s) for +: 'Foo' and 'Foo'
We can enable addition by defining the __add__ method.
In [4]: class Foo:
...: def __init__(self, x):
...: self.x = x
...: def __add__(self, other):
...: return Foo(self.x + other.x)
...:
In [5]: Foo(3) + Foo(5)
Out[5]: <__main__.Foo at 0x102bdc2e8>
to make clear that it worked
In [6]: result = Foo(3) + Foo(5)
In [7]: result.x
Out[7]: 8
But this doesn't solve everything.
In [8]: sum([Foo(3), Foo(5)])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-70968119f3ba> in <module>()
----> 1 sum([Foo(3), Foo(5)])
TypeError: unsupported operand type(s) for +: 'int' and 'Foo'
We didn't try to add an int, but the sum function thinks we did. What gives?
THE sum FUNCTION
using ipython to inspect the sum function, you can see that it includes an
optional start parameter
In [1]: sum??
Docstring:
sum(iterable[, start]) -> value
Return the sum of an iterable of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0). When the iterable is
empty, return start.
Type: builtin_function_or_method
So according to that, sum(s) is the same as sum(s, 0) and it's this start
value that is causing the error. All we have to do is replace the start value
with the equivalent Foo object
In [9]: sum([Foo(3), Foo(5)], Foo(0))
Out[9]: <__main__.Foo at 0x102bdc9e8>
In [10]: result = sum([Foo(3), Foo(5)], Foo(0))
In [11]: result.x
Out[11]: 8
This also applies to some other types
In [12]: sum([[1,2,3], [4,5,6]], [])
Out[12]: [1, 2, 3, 4, 5, 6]
but not all of them
In [13]: sum(["abc", "def"], "")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-13-452a33de0457> in <module>()
----> 1 sum(["abc", "def"], "")
TypeError: sum() can't sum strings [use ''.join(seq) instead]
来源:https://stackoverflow.com/questions/31574541/why-sum-does-not-have-the-key-arg