Which One Use: List Comprehension Or Map Part 2?

Author: Szymon Lipiński
Published at: 2016-10-07

In the previous blog post I have presented results of benchmarking list comprehensions and maps. In this one I will run similar tests, but I will filter the list to use only the even numbers.

I will not repeat the test description from the previous article.

The Test Logic

The logic is simple: sum all squares of EVEN numbers from (1..max). So basically:

data = list(range(1, MAX_NUMBER+1))

@profile
def sum_numbers(data):
    res = 0
    # different algorithms go here
    return res

A couple of remarks:

The original list contains the same number of elements as in the previous post. I will just filter it to use only the even ones.

Filtering will be done using normal if or if in list comprehension or filter() when using map().

The filtering expression is: number % 2 == 0.

For Python 2 I used: Using: 2.7.12 (default, Jul 1 2016, 15:12:24) [GCC 5.4.0 20160609].

For Python 3 I used: Using: 3.5.2 (default, Sep 10 2016, 08:21:44) [GCC 5.4.0 20160609].

I will not present the full results, instead just a table at the end.

Simple For Loop

This is the simplest, and the most common, way of iterating a container. Just make a for loop, and iterate.

@profile
def sum_numbers(data):
    res = 0
    for x in data:
        if x % 2 == 0:
            res += x*x
    return res

List Comprehension Squared With For Loop

Here we have a list comprehension, which creates a list of original values. The values are then squared, and summed.

@profile
def sum_numbers_list_comprehension_for_square(data):
    res = 0
    for x in [n for n in data if n % 2 == 0]:
        res += x*x
    return res

List Comprehension With For Loop Squaring

Here we have a list comprehension, which creates a list of squared values, and then it is summed using the for loop.

@profile
def sum_numbers_list_comprehension_for_square(data):
    res = 0
    for x in [n*n for n in data if n % 2 == 0]:
        res += x
    return res

List Comprehension With Sum()

There is a list comprehension, which creates a list of squared values, and then it is summed using the sum function.

@profile
def sum_numbers_list_comprehension_squared_sum(data):
    res = 0
    res = sum([n*n for n in data if n % 2 == 0])
    return res

Map With For Loop Squaring

Here we have the map() function, which returns in fact an original values. They are then squared, and summed in a for loop.

@profile
def sum_numbers_map_for_square(data):
    res = 0
    for x in map(lambda n: n, filter(lambda m: m % 2 == 0, data)):
        res += x*x
    return res

Map Squared With For Loop

Here the map returns squared values, which are summed using the for loop.

@profile
def sum_numbers_map_squared_for(data):
    res = 0
    for x in map(lambda n: n*n, filter(lambda m: m % 2 == 0, data)):
        res += x
    return res

Map Squared With Sum

And the most common functional way of implementing this: map returning squared values, which are summed using the sum function.

@profile
def sum_numbers_map_squared_sum(data):
    res = 0
    res = sum(map(lambda n: n*n, filter(lambda m: m % 2 == 0, data)))
    return res

SUMMARY

Lot’s of data. Let’s sum it up.

The data from the previous post (so all functions without filtering):

Test Name Python Version Time [s] Memory Jump [MB]
simple for loop 2 68.41 0.0
simple for loop 3 76.08 0.0
compr. for sq. 2 105.65 9.7
compr. for sq. 3 117.45 7.8
compr. sq. for 2 105.33 9.2
compr. sq. for 3 117.23 7.8
compr. sum 2 34.68 41.6
compr. sum 3 37.74 38.8
map for. sq. 2 139.67 7.6
map for. sq. 3 150.95 0.0
map sq. for 2 141.77 31.1
map sq. for 3 159.23 0.0
map sq. sum 2 71.23 31.1
map sq. sum 3 77.07 0.0

This time data:

Test Name Python Version Time [s] Memory Jump [MB]
simple for loop 2 86.43 0.0
simple for loop 3 95.12 0.0
compr. for sq. 2 70.00 3.7
compr. for sq. 3 76.40 3.9
compr. sq. for 2 70.21 17.0
compr. sq. for 3 77.28 19.5
compr. sum 2 35.70 17.0
compr. sum 3 38.39 19.4
map for. sq. 2 146.33 11.5
map for. sq. 3 155.91 0.0
map sq. for 2 138.70 23.3
map sq. for 3 156.66 0.0
map sq. sum 2 109.05 8.6
map sq. sum 3 119.87 0.0

I’m interested only in the memory jump inside the test function. I ignore if the memory decreased later.

A couple of remarks:

The simple for loop is not the best.

The memory overhead is much smaller than without filtering, which is not a surprise, as we need to store half of the data.

Map is still much slower than list comprehension.

Map on Python 3 has almost no overhead - which is obvious when you read the documentation and find out that the filter and the map are generators, so they return one value at a time instead of building the whole list.

The list comprehension has memory overhead, as it builds a new list, so generally it should be slower, and has bigger memory usage.

Python 3 is slower than Python 2.

FINAL REMARK

Final, and the most important.

That’s not true that

you should use list comprehension bacause that’s more pythonic.

The truth is

You should use list comprehensions, not map, bacause it is MUCH FASTER

Howgh

The comments are disabled. If you want to write something to me, you can use e.g. Twitter.