Map vs List comprehension in Python
Tags: python, cpython, map
Published: 2018-10-27 14:55:24

---
title: Map vs List comprehension in Python
published: true
description: Map vs List comprehension. Which one to use?
tags: python, cpython, map
---

So, I was trying to learn Elm (I just gave up), then I saw this feature request:
{% github https://github.com/elm/compiler/issues/147 %}
I was trying to find an alternative to `list comprehension` in Elm, they was recommending to use `map`. List comprehension is just a [Syntactic Sugar](https://www.wikiwand.com/en/Syntactic_sugar). Then let's compare both of them in Python.

# Performance
`map` performance:
```python
lambdef lab λ python -m timeit "map(lambda a: a*a, range(100))"
100000 loops, best of 3: 11.5 usec per loop
```

`List comprehension` performance:
```python
lambdef lab λ python -m timeit "[a*a for a in range(100)]"     
100000 loops, best of 3: 5.51 usec per loop
```

`List comprehension` is 2x faster!

# Syntactical comparison
Actually, `List comprehension` looks more concise and beautiful than map.
For example:
```python
[n for n in nlist]|
                  |
                  |
map(lambda n: n, nlist)
```
It's 5 bytes larger and slower! Bad!

# Bytecode generation
There is [dis](https://docs.python.org/3.7/library/dis.html) to show Python Bytecode of Python code. Let's see the bytecode of `list comprehension` and `map` to compare the performance.

```python
Bytecode of [n for n in range(100)] (List comprehension)

  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x7f042c8eac00, file "<dis>", line 1>)
              2 LOAD_CONST               1 ('<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_NAME                0 (range)
              8 LOAD_CONST               2 (100)
             10 CALL_FUNCTION            1
             12 GET_ITER
             14 CALL_FUNCTION            1
             16 RETURN_VALUE
```
It loads list comprehension, creates function, loads range function and loads 100 to give it as argument, calls range function, and returns the value.

```python
Bytecode of map(lambda n: n, range(100)) (Map)

  1           0 LOAD_NAME                0 (map)
              2 LOAD_CONST               0 (<code object <lambda> at 0x7f042c8ead20, file "<dis>", line 1>)
              4 LOAD_CONST               1 ('<lambda>')
              6 MAKE_FUNCTION            0
              8 LOAD_NAME                1 (range)
             10 LOAD_CONST               2 (100)
             12 CALL_FUNCTION            1
             14 CALL_FUNCTION            2
             16 RETURN_VALUE
```
It loads map function, it loads the lambda (`lambda n: n`), makes the lambda function object, loads range function and loads 100 to give it as argument, calls two functions and returns the value.

In my opinion, Python Bytecode is understandable by humans™.

# Pros & Cons

### List comprehension
- Pros
  * Support for generating lists directly
  * Support for generating generators directly
  * Filtering (ifs)
  * Concise
  * Fast
- Cons
  * No cons! (IMO)

### Map
- Pros
  * No pros! (IMO)
- Cons
  * Generates **only** map object (You need to turn it into the object you want later)
  * No ifs (filtering)
  * Slower
  * Verbose

# Code examples

* Double each number to 100
```python
# List comprehension
[n*2 for n in range(100)]

# Map
map(lambda n: n*2, range(100))
```

* Apply `fn` to each number to 100
```python
# List comprehension
[fn(n) for n in range(100)]

# Map
map(fn, range(100))
```
* Filter evens
```python
# List comprehension
[n for n in range(100) if n % 2 == 0]

# Map
not_possible()
# But you can use filter:
# filter(lambda n: n % 2 == 0)
```

# Conclusion
Use `list comprehension` over `map` while optimizing your code.

Thanks for reading!