- List Comprehension
Python features functional programming tools like map and filter for mapping operations over sequences and collecting results.
Since this is such a common task in Python coding, Python made a new expression: the list comprehension which is more flexible than map and filter. List comprehensions apply an arbitrary expression to items in an iterable rather than applying function. It provides a compact way of mapping a list into another list by applying a function to each of the elements of the list.
Python's built-in ord returns the ASCII integer code of a character:
>>> ord('A') 65
If we want to collect the ASCII codes of all characters in a string, the most straightforward method is using a for loop and append the results to a list:
>>> result =  >>> for x in 'Dostoyevsky': result.append(ord(x)) >>> result [68, 111, 115, 116, 111, 121, 101, 118, 115, 107, 121] >>>
If we use map, we can get the same result with a single function call:
>>> result = list(map(ord,'Dostoyevsky')) >>> result [68, 111, 115, 116, 111, 121, 101, 118, 115, 107, 121] >>>
But, we can get the similar result from a list comprehension expression. While map maps a function over a sequence, list comprehensions map an expression over a sequence:
>>> result = [ord(x) for x in 'Dostoyevsky'] >>> result [68, 111, 115, 116, 111, 121, 101, 118, 115, 107, 121] >>>
List comprehensions collect the result of applying an arbitrary expression to a sequence and return them in a new list. The effect is similar to that of the for loop and the map call. List comprehensions become more convenient when we need to apply an arbitrary expression to a sequence:
>>> [x ** 3 for x in range(5)] [0, 1, 8, 27, 64]
If we had to use map, we would need to write a function to implement the square operation, probably, lambda instead of using a def:
>>> list(map((lambda x: x ** 2),range(5))) [0, 1, 4, 9, 16]
This does the job. It's only a little bit longer that the list comprehension. For more advance kinds of expressions, however, list comprehensions will often require considerably less typing.
If we use if with list compression, it is almost equivalent to the filter built-in.
Let's make examples using both schemes.
>>> >>> [x for x in range(10) if x % 2 == 0] [0, 2, 4, 6, 8] >>> list(filter((lambda x: x % 2 == 0), range(10))) [0, 2, 4, 6, 8] >>> result =  >>> for x in range(10): if x % 2 == 0: result.append(x) >>> result [0, 2, 4, 6, 8] >>>
All of these use the modulus operator %, to extract even numbers. The filter call here is not much longer that the list comprehension either. But we can combine an if and a map, in a single expression:
>>> [x ** 2 for x in range(10) if x % 2 == 0] [0, 4, 16, 36, 64] >>>
We collect the squares of the even numbers from 0 to 9. The for loop skips numbers which the if on the right is false. The expression on the left computes the squares. The equivalent map requires a lot more work. We have to combine filter with map iteration:
>>> list( map((lambda x: x ** 2), filter((lambda x: x % 2== 0),range(10))) ) [0, 4, 16, 36, 64]
Actually, list comprehensions are more general. We can code any number of nested for loop in a list comprehension, and each may have an optional associated if test. When for loop are nested within a list comprehension, they work like equivalent for loop statement:
>>> result =  >>> result = [ x ** y for x in [10, 20, 30] for y in [2, 3, 4]] >>> result [100, 1000, 10000, 400, 8000, 160000, 900, 27000, 810000] >>>
More verbose version is:
>>> result =  >>> for x in [10, 20, 30]: for y in [2, 3, 4]: result.append(x ** y) >>> result [100, 1000, 10000, 400, 8000, 160000, 900, 27000, 810000] >>>
Though list comprehensions construct lists, they can iterate over any sequence:
>>> [x + y for x in 'ball' for y in 'boy'] ['bb', 'bo', 'by', 'ab', 'ao', 'ay', 'lb', 'lo', 'ly', 'lb', 'lo', 'ly'] >>>
Here is a much more complicated list comprehension example:
>>> [(x,y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1] [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
The expression permutes even numbers from 0 through 4 with odd numbers from 0 through 4. Here is an equivalent version which is much more verbose:
>>> result =  >>> for x in range(5): if x % 2 == 0: for y in range(5): if y % 2 == 1: result.append((x,y)) >>> result [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)] >>>
Let's see how the list comprehension works with Matrixes. For example, we have two 3 x 3 matrixes as lists of nested list:
>>> M1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] >>> M2 = [[9, 8, 7], [6, 5, 4], [3, 2, 1]]
We can index rows and columns within rows:
>>> M1 [7, 8, 9] >>> M1 9
List comprehensions are powerful tools for processing such structures because they automatically scan rows and columns for us. For example, though this structure stores the matrix by rows, to collect the second column we can simply iterate across the rows and pull out the desired column. We can also iterate through positions in the rows and index as we go:
>>> [r for r in M1] [3, 6, 9]
Here, we pulled out column 3 from M1. We can get the same result from the following list comprehension.
>>> [M1[r] for r in (0, 1, 2)] [3, 6, 9]
We typically should use simple for loops when getting started with Python, and map. Use comprehension where they are easy to apply. However, there is a substantial performance advantage to use list comprehension. The map calls are roughly twice as fast as equivalent for loops. List comprehensions are usually slightly faster than map calls. This speed difference is largely due to the fact that map and list comprehensions run at C language speed inside the interpreter. It is much faster that stepping through Python for loop code within the Python Virtual Machine (PVM).
However, for loops make logic more explicit, we may want to use them on the grounds of simplicity. On the other hand, map and list comprehensions are worth knowing and using for simpler kinds of iterations if the speed of application is an important factor.
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization