366

This is how I am accustomed to filter, map, and reduce working in Python 2:

>>> def f(x):
        return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

>>> def cube(x):
        return x*x*x
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

>>> def add(x,y):
        return x+y
>>> reduce(add, range(1, 11))
55

However, all of these seem to break in Python 3:

>>> filter(f, range(2, 25))
<filter object at 0x0000000002C14908>

>>> map(cube, range(1, 11))
<map object at 0x0000000002C82B70>

>>> reduce(add, range(1, 11))
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    reduce(add, range(1, 11))
NameError: name 'reduce' is not defined

Why are the results different? How can I get Python 3 code to work like the Python 2 code did?


See also: What is the problem with reduce()? for specific motivation for the change to put reduce into a standard library module rather than leaving it as a builtin.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Dick Lucas
  • 12,289
  • 14
  • 49
  • 76
  • 1
    In short, list is not the only datatype. If you want a list, say you want a list. But in most cases, you want something else anyway. – Veky Dec 18 '18 at 08:02

7 Answers7

382

You can read about the changes in What's New In Python 3.0. You should read it thoroughly when you move from 2.x to 3.x since a lot has been changed.

The whole answer here are quotes from the documentation.

Views And Iterators Instead Of Lists

Some well-known APIs no longer return lists:

  • [...]
  • map() and filter() return iterators. If you really need a list, a quick fix is e.g. list(map(...)), but a better fix is often to use a list comprehension (especially when the original code uses lambda), or rewriting the code so it doesn’t need a list at all. Particularly tricky is map() invoked for the side effects of the function; the correct transformation is to use a regular for loop (since creating a list would just be wasteful).
  • [...]

Builtins

  • [...]
  • Removed reduce(). Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.
  • [...]
Community
  • 1
  • 1
nhahtdh
  • 55,989
  • 15
  • 126
  • 162
  • 32
    Adding `list(map(...) ` everywhere .. how in the world is that helping readability.. `python` can't seem to handle progressive / streaming application of functional combinators. Other languages I can chain a dozen operations against a collection in a row and it's readable. Here? what do you want - a dozen way nested `in` ?? – WestCoastProjects Feb 27 '18 at 05:29
  • 11
    If you're working in an imperative context, then a for-loop is probably the more readable option. But there are good reasons to prefer a functional context--and breaking from that to go back to procedural can be pretty darn ugly. – MatrixManAtYrService Jun 27 '18 at 15:08
  • 3
    @javadba Are you sure in a "streaming application" you need to add the `list` call at all? I thought the meaning of "streaming" is "no list is created at all; process each element of the input fully before moving on to the next". – Imperishable Night Oct 05 '18 at 02:19
  • @MatrixManAtYrService If you are sure the python 2 behavior is what you need, you can always just redefine `map`. – Imperishable Night Oct 05 '18 at 02:22
  • 6
    I still cant grasp how a readability argument leads to such a change. If it was for performance reasons I might understand... – René Jahn Nov 08 '18 at 12:44
  • readability is one thing, but it makes (list, dict, ...)comprehensions unusable. Can't put an ordinary for loop in the middle of a list comprehension, now can I.. ugh. – Kaos Oct 25 '19 at 16:37
  • 2
    A "quick fix" (read: hack) is to use list(map...) but notice the "better fix" is to use a list comprehension instead - like [Foo(x) for x in mylist]. This doesn't lead to adding list() everywhere and longer term may be better. (@javadba FYI) – dmonopoly Nov 13 '19 at 15:52
  • @dmonopoly I specifically mentioned applying a dozen transformations and that I do not want to have a 12 way nested list comprehension - which is the only option in standard python afaik – WestCoastProjects Nov 13 '19 at 20:08
102

The functionality of map and filter was intentionally changed to return iterators, and reduce was removed from being a built-in and placed in functools.reduce.

So, for filter and map, you can wrap them with list() to see the results like you did before.

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> list(filter(f, range(2, 25)))
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> list(map(cube, range(1, 11)))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> import functools
>>> def add(x,y): return x+y
...
>>> functools.reduce(add, range(1, 11))
55
>>>

The recommendation now is that you replace your usage of map and filter with generators expressions or list comprehensions. Example:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> [i for i in range(2, 25) if f(i)]
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> [cube(i) for i in range(1, 11)]
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>>

They say that for loops are 99 percent of the time easier to read than reduce, but I'd just stick with functools.reduce.

Edit: The 99 percent figure is pulled directly from the What’s New In Python 3.0 page authored by Guido van Rossum.

Joshua D. Boyd
  • 4,808
  • 3
  • 29
  • 44
  • 6
    You do not need to create extra functions in list comprehensions. Just use `[i*i*i for i in range(1,11)]` – Xiao Jul 22 '14 at 03:25
  • 2
    You are absolutely correct. I kept the function in the list comprehension examples to keep it looking similar to the filter/map examples. – Joshua D. Boyd Sep 02 '14 at 18:52
  • 5
    i**3 is also equivalent of i\*i\*i – Breezer Jan 17 '16 at 15:13
  • 5
    @Breezer actually `i**3` will call `i.__pow__(3)` and `i*i*i` `i.__mul__(i).__mul__(i)` (or something like that). With ints it doesn't matter but with numpy numbers/custom classes it might even produce different results. – syntonym Mar 18 '16 at 11:06
  • @JoshuaD.Boyd But which is faster? A `map` or `generator`? – John Strood Aug 08 '18 at 07:14
  • 2
    I have noticed that whenever we hear that "Guido made decision X" that *pain* is a likely outcome. This is a great example: `list(list(list(.. )))` to do what was *already* verbose in python. – WestCoastProjects Oct 05 '18 at 04:17
12

As an addendum to the other answers, this sounds like a fine use-case for a context manager that will re-map the names of these functions to ones which return a list and introduce reduce in the global namespace.

A quick implementation might look like this:

from contextlib import contextmanager    

@contextmanager
def noiters(*funcs):
    if not funcs: 
        funcs = [map, filter, zip] # etc
    from functools import reduce
    globals()[reduce.__name__] = reduce
    for func in funcs:
        globals()[func.__name__] = lambda *ar, func = func, **kwar: list(func(*ar, **kwar))
    try:
        yield
    finally:
        del globals()[reduce.__name__]
        for func in funcs: globals()[func.__name__] = func

With a usage that looks like this:

with noiters(map):
    from operator import add
    print(reduce(add, range(1, 20)))
    print(map(int, ['1', '2']))

Which prints:

190
[1, 2]

Just my 2 cents :-)

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • 1
    `python` as a language is a mess - but it has v good to excellent libraries: `numpy`, `pandas`, `statsmodels` and friends.. I had been buliding convenience libraries like you show here to reduce the pain of the native language - but have lost the energy and try not to stray far from a `data.frame` / `datatable`, or `xarray`. But kudos for trying.. – WestCoastProjects Oct 05 '18 at 04:22
10

Since the reduce method has been removed from the built in function from Python3, don't forget to import the functools in your code. Please look at the code snippet below.

import functools
my_list = [10,15,20,25,35]
sum_numbers = functools.reduce(lambda x ,y : x+y , my_list)
print(sum_numbers)
O.O.Balance
  • 2,930
  • 5
  • 23
  • 35
Bikash Singh
  • 291
  • 4
  • 3
4

One of the advantages of map, filter and reduce is how legible they become when you "chain" them together to do something complex. However, the built-in syntax isn't legible and is all "backwards". So, I suggest using the PyFunctional package (https://pypi.org/project/PyFunctional/). Here's a comparison of the two:

flight_destinations_dict = {'NY': {'London', 'Rome'}, 'Berlin': {'NY'}}

PyFunctional version

Very legible syntax. You can say:

"I have a sequence of flight destinations. Out of which I want to get the dict key if city is in the dict values. Finally, filter out the empty lists I created in the process."

from functional import seq  # PyFunctional package to allow easier syntax

def find_return_flights_PYFUNCTIONAL_SYNTAX(city, flight_destinations_dict):
    return seq(flight_destinations_dict.items()) \
        .map(lambda x: x[0] if city in x[1] else []) \
        .filter(lambda x: x != []) \

Default Python version

It's all backwards. You need to say:

"OK, so, there's a list. I want to filter empty lists out of it. Why? Because I first got the dict key if the city was in the dict values. Oh, the list I'm doing this to is flight_destinations_dict."

def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):
    return list(
        filter(lambda x: x != [],
               map(lambda x: x[0] if city in x[1] else [], flight_destinations_dict.items())
               )
    )
Daniel
  • 3,758
  • 3
  • 22
  • 43
  • Would it also work to try something like: ```python def find_return_flights(city): return [key for key, val in flight_destinations_dict.items() if city in val] ``` – dslack Mar 06 '21 at 11:37
  • It probably would, but that wouldn't be strictly speaking functional programming. – Daniel Mar 07 '21 at 13:45
1
from functools import reduce

def f(x):
    return x % 2 != 0 and x % 3 != 0

print(*filter(f, range(2, 25)))
#[5, 7, 11, 13, 17, 19, 23]

def cube(x):
    return x**3
print(*map(cube, range(1, 11)))
#[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

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

reduce(add, range(1, 11))
#55

It works as is. To get the output of map use * or list

Golden Lion
  • 3,840
  • 2
  • 26
  • 35
0

Here are the examples of Filter, map and reduce functions.

numbers = [10,11,12,22,34,43,54,34,67,87,88,98,99,87,44,66]

//Filter

oddNumbers = list(filter(lambda x: x%2 != 0, numbers))

print(oddNumbers)

//Map

multiplyOf2 = list(map(lambda x: x*2, numbers))

print(multiplyOf2)

//Reduce

The reduce function, since it is not commonly used, was removed from the built-in functions in Python 3. It is still available in the functools module, so you can do:

from functools import reduce

sumOfNumbers = reduce(lambda x,y: x+y, numbers)

print(sumOfNumbers)

Yogendra Singh
  • 2,063
  • 25
  • 20