0

I am trying to learn python reduce function.

This is some code that does not make sense to me

>>> x = [1,2,3]
>>> reduce(sum, [b for b in x if b > 0])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> reduce(sum, [b for b in x if b > 2])
3

Why does it work when b > 2 but not b > 0

The code seems almost exactly the same

1
  • You may also want to learn filter, that list comprehension is unnecessarily verbose Commented Jun 11, 2016 at 15:57

5 Answers 5

3

In the second case the list you created with list comprehension has only one element, so it is not reduced. This reduction doesn't fail, because there is nothing to be reduced there, so function sum isn't executed and it doesn't fail. The result is the only integer, that has been there before.
The function sum is defined in the documentation. As you can see, it is supposed to sum values in some iterable container, such as list or tuple. You could write sum([1,2,3]) and it would work just like this reduction, that you want to make. If you want to achieve this result with using the reduction function, you have to make sure, that you have a function, that takes two integers and returns their sum.

Here you have such function:

def sum2(x, y):
    return x + y

Reduce with such function should give you expected results.

sum2 = lambda x, y: x+y

The following function does the same, but is shorter and nicer due to lambda notation, it might be nice to look at it sometime in the future, if you don't know it yet.

Sign up to request clarification or add additional context in comments.

Comments

2

From the docs about reduce:

Apply function of two arguments cumulatively to the items of iterable, from left to right, so as to reduce the iterable to a single value

So in the first case, you have used reduce wrongly by using the builtin sum which does not take two arguments but an iterable. sum consumes the iterable in the first iteration of reduce and returns an int which is not further reduceable.


In the second case, sum is not even called on the iterable:

If initializer is not given and iterable contains only one item, the first item is returned.

Since iterable is of length 1, reduce returns the one item in the iterable and sum does not nothing.

Comments

2

Input in second code snippet is single element list - it's a degenerated case where callable isn't even called.

Consider following example:

def my_sum(*args):
    print "my_sum called with", args
    return sum(*args)


x = [1, 2, 3]
reduce(my_sum, [b for b in x if b > 2])
# nothing gets printed, value 3 is returned, since it's only element in list

Now consider your failed case:

reduce(my_sum, [b for b in x if b > 0])

Output is:

my_sum called with (1, 2)
[exception traceback]

Equivalent call to builtin sum is sum(1, 2), which results in same exception as quoted by you.

sum signature does not follow rules for reduce function. sum(iterable[, start]) takes two arguments (as expected), but first of them has to be iterable, where second is optional initial value.

reduce requires function to take two arguments, where (quoting docs):

The left argument, x, is the accumulated value and the right argument, y, is the update value from the iterable.

We can clearly see that these interfaces are not conform with each other. Correct callable will be something like:

def reduce_sum(accumulator, new_value):
    return accumulator + new_value

reduce(reduce_sum, [b for b in x if b > 0])

Comments

1

As Moses points out "sum consumes the iterable in the first iteration" so let's play with it

>>> sum(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

>>> sum([1,2])
3
>>> sum(x)
6
>>> sum([b for b in x if b >0])
6
>>> reduce(lambda x, y: x+y, x)
6
>>> reduce(lambda x, y: x+y, [b for b in x if b > 0])
6
>>> reduce(lambda x, y: x+y, [b for b in x if b > 1])
5

Comments

1

The reduce function takes in a function and an iterable.
It then maps each element of that iterable to the function.


What your first b > 0 function is doing is trying to run sum with 2 arguments. One argument being the accumulated total, and the other being an element of the input list, for example it would look like: sum(0, 1).
This is stated in the documentation as:

Apply function of two arguments cumulatively to the items of iterable, from left to right, so as to reduce the iterable to a single value. ... The left argument, is the accumulated value and the right argument, is the update value from the iterable.


What your second b > 2 function is doing is simply running the reduce function with an iterator containing one item, which does not attempt to apply sum as stated in the documentation.

If initializer is not given and iterable contains only one item, the first item is returned.

But since sum takes in an iterable, you could just do:

sum([b for b in x if b > 0])

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.