4.1. Use Comprehensions Instead of map and filter

Python provides compact syntax for deriving a new list from another sequence or iterable. These expressions are called list comprehensions. For example, say that I want to compute the square of each number in a list. Here, I do this by using a simple for loop:

>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> squares = []
>>> for x in a:
>>>     squares.append(x**2)
>>> print(squares)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

With a list comprehension, I can achieve the same outcome by specifying the expression for my computation along with the input sequence to loop over:

>>> squares = [x**2 for x in a]  # List comprehension
>>> print(squares)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Unless you’re applying a single-argument function, list comprehensions are also clearer than the map built-in function for simple cases. map requires the creation of a lambda function for the computation, which is visually noisy:

>>> alt = map(lambda x: x ** 2, a)

Unlike map, list comprehensions let you easily filter items from the input list, removing corresponding outputs from the result. For example, say I want to compute the squares of the numbers that are divisible by 2. Here, I do this by adding a conditional expression to the list comprehension after the loop:

>>> even_squares = [x**2 for x in a if x % 2 == 0]
>>> print(even_squares)
[4, 16, 36, 64, 100]

The filter built-in function can be used along with map to achieve the same outcome, but it is much harder to read:

>>> alt = map(lambda x: x**2, filter(lambda x: x % 2 == 0, a))
>>> assert even_squares == list(alt)

Dictionaries and sets have their own equivalents of list comprehensions (called dictionary comprehensions and set comprehensions, respectively). These make it easy to create other types of derivative data structures when writing algorithms:

>>> even_squares_dict = {x: x**2 for x in a if x % 2 == 0}
>>> threes_cubed_set = {x**3 for x in a if x % 3 == 0}
>>> print(even_squares_dict)
>>> print(threes_cubed_set)
{2: 4, 4: 16, 6: 36, 8: 64, 10: 100}
{216, 729, 27}

Achieving the same outcome is possible with map and filter if you wrap each call with a corresponding constructor. These statements get so long that you have to break them up across multiple lines, which is even noisier and should be avoided:

>>> alt_dict = dict(map(lambda x: (x, x**2),
>>>                 filter(lambda x: x % 2 == 0, a)))
>>> alt_set = set(map(lambda x: x**3,
>>>               filter(lambda x: x % 3 == 0, a)))

4.1.1. Things to Remember

✦ List comprehensions are clearer than the map and filter built-in functions because they don’t require lambda expressions.

✦ List comprehensions allow you to easily skip items from the input list, a behavior that map doesn’t support without help from filter.

✦ Dictionaries and sets may also be created using comprehensions.