67
vec = [[1,2,3], [4,5,6], [7,8,9]]
print [num for elem in vec for num in elem]      <----- this

>>> [1, 2, 3, 4, 5, 6, 7, 8, 9]

This is tricking me out.
I understand elem is the lists inside of the list from for elem in vic
I don't quite understand the usage of num and for num in elem in the beginning and the end.

How does python interpret this?
What's the order it looks at?

Martijn Pieters
  • 889,049
  • 245
  • 3,507
  • 2,997
ealeon
  • 10,271
  • 16
  • 74
  • 143
  • possible duplicate of [Double Iteration in List Comprehension](http://stackoverflow.com/questions/1198777/double-iteration-in-list-comprehension) – Inbar Rose Jul 15 '13 at 15:21
  • 3
    That is not that great a dupe target, @Inbar, because it fails to explain anything about how Python interprets the order of the nested loops. – Martijn Pieters Jul 15 '13 at 15:27
  • Related / possible dupe: [nested list comprehensions](http://stackoverflow.com/a/11934483) – Martijn Pieters Jul 15 '13 at 15:28

4 Answers4

126

Lets break it down.

A simple list-comprehension:

[x for x in collection]

This is easy to understand if we break it into parts: [A for B in C]

  • A is the item that will be in the resulting list
  • B is each item in the collection C
  • C is the collection itself.

In this way, one could write:

[x.lower() for x in words]

In order to convert all words in a list to lowercase.


It is when we complicate this with another list like so:

[x for y in collection for x in y] # [A for B in C for D in E]

Here, something special happens. We want our final list to include A items, and A items are found inside B items, so we have to tell the list-comprehension that.

  • A is the item that will be in the resulting list
  • B is each item in the collection C
  • C is the collection itself
  • D is each item in the collection E (in this case, also A)
  • E is another collection (in this case, B)

This logic is similar to the normal for loop:

for y in collection:     #      for B in C:
    for x in y:          #          for D in E: (in this case: for A in B)
        # receive x      #              # receive A

To expand on this, and give a great example + explanation, imagine that there is a train.

The train engine (the front) is always going to be there (the result of the list-comprehension)

Then, there are any number of train cars, each train car is in the form: for x in y

A list comprehension could look like this:

[z for b in a for c in b for d in c ... for z in y]

Which would be like having this regular for-loop:

for b in a:
    for c in b:
        for d in c:
            ...
                for z in y:
                    # have z

In other words, instead of going down a line and indenting, in a list-comprehension you just add the next loop on to the end.

To go back to the train analogy:

Engine - Car - Car - Car ... Tail

What is the tail? The tail is a special thing in list-comprehensions. You don't need one, but if you have a tail, the tail is a condition, look at this example:

[line for line in file if not line.startswith('#')] 

This would give you every line in a file as long as the line didn't start with a hashtag (#), others are just skipped.

The trick to using the "tail" of the train is that it is checked for True/False at the same time as you have your final 'Engine' or 'result' from all the loops, the above example in a regular for-loop would look like this:

for line in file:
    if not line.startswith('#'):
        # have line

please note: Though in my analogy of a train there is only a 'tail' at the end of the train, the condition or 'tail' can be after every 'car' or loop...

for example:

>>> z = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
>>> [x for y in z if sum(y)>10 for x in y if x < 10]
[5, 6, 7, 8, 9]

In regular for-loop:

>>> for y in z:
    if sum(y)>10:
        for x in y:
            if x < 10:
                print x

5
6
7
8
9
Inbar Rose
  • 35,719
  • 22
  • 80
  • 120
  • Your answer implies you can only use an `if` statement at the end of a list comprehension. That is **not** true. You can use if statements at *any* level in your list comprehensions, even put several after one another (albeit that that looks a little nonsensical until you realize they are nested). You just cannot use an `if` as the *first* item, that has to be a `for` loop. – Martijn Pieters Jul 15 '13 at 16:00
  • 2
    You are right, I did not mean to imply that, I will make it more clear. – Inbar Rose Jul 15 '13 at 16:01
  • I must say that your explanation vastly overcomplicates things. Especially when you start expanding `A for B in C for D in E` into a nested loop with `for B in C:` and `for A in B`, discarding the `for D in E` part. – Martijn Pieters Jul 15 '13 at 16:04
  • @Martin: Can't you use an if statement before the for loop in the following form? A=[[[y for y in xrange(3)],2][x<=3] for x in xrange(7)] – Stefan Gruenwald Feb 15 '15 at 06:34
9

From the list comprehension documentation:

When a list comprehension is supplied, it consists of a single expression followed by at least one for clause and zero or more for or if clauses. In this case, the elements of the new list are those that would be produced by considering each of the for or if clauses a block, nesting from left to right, and evaluating the expression to produce a list element each time the innermost block is reached.

In other words, pretend that the for loops are nested. Reading from left to right your list comprehension can be nested as:

for elem in vec:
    for num in elem:
        num           # the *single expression* from the spec

where the list comprehension will use that last, innermost block as the values of the resulting list.

Martijn Pieters
  • 889,049
  • 245
  • 3,507
  • 2,997
6

Your code equals:

temp = []
for elem in vec:
    for num in elem:
        temp.append(num)
zhangyangyu
  • 8,052
  • 2
  • 30
  • 42
  • 6
    Note that the `for` statements are written in the list comprehension in the same order they'd be written if you just wrote regular `for` loops as above. – kindall Jul 15 '13 at 15:23
1

You can look at list comprehension just as sequential statements. This applies for any levels of for and if statements.

For example, consider double for loop with their own ifs:

vec = [[1,2,3], [4,5,6], [7,8,9]]
result = [i for e in vec if len(e)==3 for i in e if i%2==0]

Here the list comprehension is same as:

result = []
for e in vec: 
    if len(e)==3:
        for i in e:
            if i%2==0:
                result.append(i)

As you can see list comprehension is simply for and if without indentations but in same sequence.

Shital Shah
  • 47,549
  • 10
  • 193
  • 157