An Introduction to List Comprehensions in Python

1_Gk-Z0_C6LU0MW4FDYV0Lww.png

Pythonic way of expressing iterative logic

A few years back when I first started learning Python, I was kinda blown away by its succinctness and its expressiveness. Talking about myself, I had a background in C-style procedural languages, before I started learning Python.

One particular distinctive piece of syntax inside Python that I found particularly interesting was list comprehensions. Coming from a background in C-style programming languages, I had never seen a piece of syntax like it before and it kinda blew my mind. It is a way of iterating through a list and transforming each element of that list and compiling a new list having these transformed elements, in a single line of code! List comprehensions are not the only way of accomplishing this particular use case, we can possibly use a traditional for loop (though not in a single line) or we can also use a functional-style transformation function like ‘map’.

List comprehension has its counterparts for other types of Python data structures like for sets we have set comprehensions and for dictionaries, we have dictionary comprehensions.

List Comprehension Usage Examples

First, let’s see how iterating through an ordered sequence of elements works in a traditional procedural way. Say we have a list of numbers.

                numbers = [1, 2, 3, 4, 5, 6]
            

Now, if we want to square each of the element of this list and create a new list containing these squares, one of the ways we can accomplish this is by using a ‘for’ loop. I think coming from a background in traditional C-style procedural languages, this is how I would have thought of accomplishing this particular task. (especially at a time when I did not know much about functional programming)

                squares_of_numbers = []
for number in numbers:
    squares_of_numbers.append(number * number)
            

Now, let’s see how we can accomplish the same using a list comprehension.

                squares_of_numbers = [n * n for n in numbers]
            

In the above piece of Python syntax, for each of the elements ’n’ inside an iterable ‘numbers’, we execute an expression ‘n*n’ (square) and compile a list containing the results of these expressions. The format of Python comprehension can be expressed as below.

                result_list = [expression for element in iterable]
            

In the above format,

  • ‘expression’ can possibly be a function call that accepts a single element and also returns a single element, for example ‘square(x)’, that takes in an element ‘x’ and returns its square. ‘expression’ can also be a Python expression that results in a single element, for example, ‘x * x’.
  • ‘element’ is each item inside ‘iterable’ that we are iterating through.
  • ‘iterable’ can be a Python data structure like list, set or dictionary.

Apart from the above elements, Python comprehension can also contain other syntax elements depending on the use case.

Now, let’s see how we can call a function inside comprehension. Say we want to create a list of square roots of each element inside another list. As we can see below, we can accomplish this using a list comprehension inside which we call the ‘sqrt’ function (‘math’ package) for each of the elements inside the ‘numbers’ list.

                from math import sqrt

numbers = [1, 2, 3, 4]
square_roots_of_numbers = [sqrt(n) for n in [1, 2, 3, 4]]
            

Now let’s see what else can we possibly accomplish using Python comprehension. Say we have two equal-sized lists called numbers1 and numbers2 containing numbers (what else !). Now, we want to create a new list containing the products of each of the elements inside these two lists. In algebraic terms, this operation is called calculating the ‘dot product’ of two vectors (lists).

                numbers1 = [1, 2, 3, 4]
numbers2 = [2, 3, 4, 5]
dot_product = [n1 * n2 for n1 in numbers1 for n2 in numbers2]
            

After executing the above piece of code, the variable ‘dot_product’ should contain the below value.

                [2, 3, 4, 5, 4, 6, 8, 10, 6, 9, 12, 15, 8, 12, 16, 20]
            

In the above piece of syntax, the expression ‘n1 * n2’ is calculating the product of each element n1 and n2 of the two iterables ‘numbers1’ and ‘numbers2'. The second ‘for’ expression, which iterates over the elements of the iterable ‘numbers2’ is executed for each element of the other iterable ‘numbers1’. Below is a procedural counterpart of the above list comprehension example.

                numbers1 = [1, 2, 3, 4]
numbers2 = [2, 3, 4, 5]
dot_product = []
for n1 in numbers1:
    for n2 in numbers2:
        dot_product.append(n1 * n2)
            

To me the Pythonic syntax, using the list comprehension, almost feels like magic !!

Now, let’s say we want to compile a new list containing only the squares of the even elements inside another list. How can we do this in a traditional procedural way? This can be possibly expressed using the below syntax using an ‘if’ condition inside a ‘for’ loop.

                numbers = [1, 2, 3, 4]
squares_of_even_elements = []
for n in numbers:
  if n % 2 == 0:
      squares_of_even_elements.append(n * n)z
            

Now, let’s see how we can accomplish the same using the Python comprehension syntax.

                numbers= [1, 2, 3, 4]
squares_of_even_elements = [n * n for n in numbers if n % 2 == 0]
            

Great !! Isn’t it? We can succinctly express quite complicated behaviour using python comprehension. Inside the ‘if’ expression of comprehension we can also do function calls instead of simple expressions like ‘n % 2 == 0’.

Now, let’s see another variation of the above conditional list comprehension that also contains an ‘else’ part in the nested ‘if’. Let’s say we have a list containing any number of characters from the alphabet ‘a’ to ‘z’ and can also contain punctuation signs like ‘ ! ’, ‘ . ’, etc. We want to create a new list containing all the elements from the aforementioned list, with the elements containing values in-between ‘a’ and ‘z’ replaced by a ‘#’ sign. How can we do this in a traditional procedural way? This can be possibly expressed using the below syntax using an ‘if else’ condition inside a ‘for’ loop.

                chars = ['s', 'a', 'n', 'j', 'a', 'y', ' ', 's', 'a', 'c', 'h', 'd', 'e', 'v', '.']
chars_with_a_to_z_replaced = []
for char in chars:
    if char >= 'a' and char <= 'z':
        chars_with_a_to_z_replaced.append('#')
    else:
        chars_with_a_to_z_replaced.append(char)
            

The above code will produce the following value inside the chars_with_a_to_z_replaced list.

                ['#', '#', '#', '#', '#', '#', ' ', '#', '#', '#', '#', '#', '#', '#', '.']
            

Now let’s see how we can accomplish the same using a list comprehension.

                chars = ['s', 'a', 'n', 'j', 'a', 'y', ' ', 's', 'a', 'c', 'h', 'd', 'e', 'v', '.']
chars_with_a_to_z_replaced = ['#' if char >= 'a' and char <= 'z' else char
                              for char in chars]
            

In the above code, we have broken the list comprehension into multiple lines. In the above list comprehension, we use the sign ‘#’ if in case the value of the element ‘char’ is in between ‘a’ and ‘z’ otherwise we just use the actual value inside the element ‘char’.

As we can see in the above example, we can combine ‘if else’ and ‘for’ inside list comprehensions to elegantly and concisely express quite complicated logic.

Set and Dictionary Comprehensions

As mentioned earlier, we can also use the comprehension syntax for other Python data structures like Sets and Dictionaries. Let’s see some examples of these.

Say we have a list of possible duplicate numbers and we want to create a set of all unique odd numbers inside this list. We can do this using a set comprehension as we can see in the below sample code.

                numbers = [1, 2, 3, 4, 4, 5, 7, 7, 5]
unique_odd_numbers = {n for n in  if n % 2}
            

Now let’s see an example of dictionary comprehension. We want to create a dictionary containing numbers and squares of those numbers as keys and values respectively. We can do this using dictionary comprehension.

                numbers = [1, 2, 3, 4]
numbers_squres = {n: n * n for n in numbers}
            

Conclusion

As we can see in the above examples, we can express quite complicated logic concisely and elegantly using comprehensions in Python, but I think we should use them with care as we might be sacrificing readability as our comprehensions get more complicated. So, I think we should always consider the readability of our code as a metric as we try writing more and more complicated comprehension expressions in Python.


Only registered users can post comments. Please, login or signup.

Start blogging about your favorite technologies and get more readers

Join other developers and claim your FAUN account now!

Stats
51

Influence

4k

Total Hits

3

Posts

Discussed tools