Python list comprehensions - help with syntax

I struggled with list comprehensions in Python. The syntax seemed so alien and strange. What ended up helping me out most was understanding the logic behind them.

Imagine a for loop like this:

example_list = []

for x in range(10):
    example_list.append(x)

# Result: example_list == [0,1,2,3,4,5,6,7,8,9]

This loop is better written as a list comprehension. The list comprehension will retain readability and require less code.

How to do that? Start with the thing you want in the list: the values of x

example_list = [x]

for x in range(10):
    example_list.append(_)

# x has been moved from the for loop up into the list. This is step 1 - this code will not run until we complete the subsequent steps.

Now we have the end result in the list (x) we need to tell the list where the x comes from. In this case it is the values generated by iterating over range(10). We can transplant that line into the list comprehension:

example_list = [x for x in range(10)]

___ _ __ _________:
    example_list.append(_)

# Now we move the "for" logic up into the list comprehension. This is step 2 - the logic is in the list comprehension and the remains of the for loop need to be cleaned out.

Then, clear out the unnecessary content:

example_list = [x for x in range(10)]

# Result: example_list == [0,1,2,3,4,5,6,7,8,9]

I like to think of this as a sentence:

Add the value of x to this list for every integer value of x in the range 0 to 10.

List comprehensions follow a pattern. The pattern can be more complicated than our simple example, as follows:

[<the result to add to the list>  <the iterable or logic that generates the result>  <the conditions under which the result is added>]

The conditions are things like if statements. For example, the following if statement updates our for loop to produce only even numbers:

example_list = []

for x in range(10):
    if x%2 == 0:
        example_list.append(x)

# Result: example_list == [0,2,4,6,8]

If we skip to the point where the result and the for loop are in the list comprehension, we can see that the if statement is the remaining piece of our loop that needs to be transplanted:

example_list = [x for x in range(10)]

___ _ __ _________:
    if x%2 == 0:
        example_list.append(_)

Following the earlier pattern, we transplant the if statement to the end of the list comprehension:

example_list = [x for x in range(10) if x%2 == 0]

___ _ __ _________:
    __ ___ __ _:
        example_list.append(_)

Then tidy up the rest:

example_list = [x for x in range(10) if x%2 == 0]

# Result: example_list == [0,2,4,6,8]

With this information, you can build up to more complicated list comprehensions. For example, the result can be its own operation. The following code takes an existing list and sums the value of the current index and the subsequent 2 values. It also uses an if statement to prevent an out of bounds error.

source = [2,4,5,7,91,10,12,45,32,52,67]

sum_list = [sum(source[index:index+3]) for index in range(len(source)) if index+3 <= len(source)]
           #<------ THE RESULT ------>#<-------- THE ITERABLE ------->#<---- THE CONDITION ----->#

## Ouput = [11, 16, 103, 108, 113, 67, 89, 129, 151]

## Result == sum(source[index:index+3])
## Sum the values found at index n, n+1, and n+2. E.g. for index 
## 0, the individual operation would be sum([2,4,5]) for a value 
## of 11.

## Iterable = for index in range(len(source))
## Use the length of the source list to provide an iterable that
## yields values for use as the index. The index is the output of 
## this iterable which is used in the result.

## Condition = if index+3 <= len(source)
## Only use index if there are enough values left in the list to 
## sum. The last three-number grouping is sum([32,52,67]). Without 
## the if statement, the code would try to retrieve a value after 
## 67 on the next value of index and hit an out of bounds error.

Using the sentence approach, I would write this as:

Sum the values found at index n, n+1, and n+2 in the source list, where the value of n is between 0 and the length of the source list, as long as the source list is long enough to provide a value at n, n+1, and n+2.

I'm sure there is some aspect of my code that is non-pythonic, but I also hope this was helpful to someone struggling with list comprehensions. If you've made it through the basics of python, you have the information necessary to understand list comprehensions.

As my final note, there is very little as satisfying in writing python as when you build a complex list comprehension and it works first try.

20