3

I would expect the two to mean the same, from a functional equational standpoint, however:

x = [1, 2, 3]
y = ['a', 'b', 'c']

reduce(lambda x, y: x + y, zip(x, y))  # works

sum(zip(x, y))  # fails

Why is sum failing here?

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
Henry Henrinson
  • 5,203
  • 7
  • 44
  • 76

1 Answers1

5

The actual problem is, with the sum's default start value. Quoting the documentation,

Sums start and the items of an iterable from left to right and returns the total. start defaults to 0. The iterable‘s items are normally numbers, and the start value is not allowed to be a string.

But, in case of reduce, if no optional start value is given, it will use the first value in the iterable as the initializer. So, reduce is actually evaluating it like this

( ( (1, 'a') + (2, 'b') ) + (3, 'c') )

Since sum assumes start value as 0, it evaluates it like this,

0 + (1, 'a') + (2, 'b') + (3, 'c')

In this case, it tries to add 0 with a tuple and that is why you are getting

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

To fix this, pass an empty tuple, to sum's start, like this

>>> sum(zip(x, y), tuple())
(1, 'a', 2, 'b', 3, 'c')

Now, with the initial value being an empty tuple, the evaluation happens like this

() + (1, 'a') + (2, 'b') + (3, 'c')

Note: In both these case, there will be more than one intermediate tuples created. To avoid that, I would recommend flattening the data and pass it to tuple constructor as a generator expression, like this

>>> tuple(item for items in zip(x, y) for item in items)
(1, 'a', 2, 'b', 3, 'c')
Neil G
  • 32,138
  • 39
  • 156
  • 257
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • 1
    This makes one think that `sum` is actually mis-designed: `start` should default to the first item rather than to `0`. – georg Feb 20 '15 at 09:29
  • @thefourtheye Ah, very good catch, about the initial value; I didn't know you could pass a custom start value. For `sum(zip(x, y), tuple())` I am still getting "TypeError: cannot perform reduce with flexible type". Any idea why that might be? – Henry Henrinson Feb 20 '15 at 09:36
  • @georg What would `sum([])` be then? – Joel Feb 20 '15 at 09:48
  • @Joel: well, because `sum` is not generic `reduce`, it would be fair to return 0 for empty arguments. But if the args is not empty, it makes no sense to default to 0. – georg Feb 20 '15 at 09:55
  • 1
    @HenryHenrinson `TypeError` with `reduce` or `sum`? – thefourtheye Feb 20 '15 at 10:42
  • @thefourtheye with sum, for the expression: `sum(zip(x, y), tuple())`. Reduce works just fine. – Henry Henrinson Feb 20 '15 at 11:09
  • 1
    @HenryHenrinson Which version of Python you are using? I tried in Py 2.7 and 3.4 Both are working fine – thefourtheye Feb 20 '15 at 11:10
  • @thefourtheye 2.7. If I set `x=[1,2,3]; y=[2,3,4]`, sum works just fine. It's only when I switch to `y=['a','b','c']` that this fails. – Henry Henrinson Feb 20 '15 at 13:20
  • @thefourtheye I think there's something weird about my environment, then; something I'm importing might be overriding `sum` (not 100% sure). Thanks for your help, though. – Henry Henrinson Feb 21 '15 at 16:36