Search code examples
pythonpython-2.7list-comprehension

Understanding list comprehensions in Python


When reading the official tutorial, I encountered this example:

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

>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

I couldn't understand this, so I did some experiments:

>>> [num for elem in vec]
[9, 9, 9]

>>> [num for elem in (vec for num in elem)]
[9, 9, 9]

And I'm even more confused now!

In which order should I read a list comprehension?


I'm sure I haven't defined a num variable with the value 9 anywhere.

python

Output:

Python 2.7.10 (default, Oct 23 2015, 19:19:21)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

REPL session:

>>> num
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'num' is not defined

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

>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> [num for elem in vec]
[9, 9, 9]

>>> [num for elem in (vec for num in elem)]
[9, 9, 9]

Solution

  • The loops in list comprehension are read from left to right. If your list comprehension would be written as an ordinary loop, it would look something like this:

    >>> vec = [[1,2,3], [4,5,6], [7,8,9]]
    >>> l = []
    >>> for elem in vec:
    ...     for num in elem:
    ...         l.append(num)
    ...
    >>> l
    
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    In Python 2, the variables within the list comprehension share the outer scope, so num is available to be used later:

    >>> vec = [[1,2,3], [4,5,6], [7,8,9]]
    
    >>> [num for elem in vec for num in elem]
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    >>> num
    9
    

    Note that on Python 3, the behavior is different:

    >>> vec = [[1,2,3], [4,5,6], [7,8,9]]
    
    >>> [num for elem in vec for num in elem]
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    >>> num
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'num' is not defined