List Comprehension question

Francis Avila francisgavila at yahoo.com
Wed Dec 10 22:54:27 EST 2003


Mark Elston wrote in message ...
>Anyway,  I thought I was following the discussions of List
>Comprehension (LC) until I got to Recipe 1.16.  In this recipe
>we have the following:
>
>    arr = [[1,2,3], [4,5,6], [7,8,9], [10,11,12]]
>    print [[r[col] for r in arr] for col in range(len(arr[0]))]
>       -> [[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]

All the second line is saying, is, for every pass of the innermost (here,
rightmost) for-loop, execute this list comprehension: [r[col] for r in arr].

Same as:

res = []
for col in range(3):
    res.append([r[col] for r in arr])

Unrolling a bit more:

res = []
for col in range(3):
    innerres = []
    for r in arr:
        innerres.append(r[col])
    res.append(innerres)

Note this is NOT the same as [r[col] for col in range(3) for r in arr]!

res = []
for col in range(3):
    for r in arr:
        res.append(r[col])

-> [1, 4, 7, 10, 2, 5, 8, 11, 3, 6, 9, 12]
See? It's flattened, because the list comp's expression evaluates to an
integer instead of a list.

>I thought I understood this until I got to the above recipe.  Here
>it looks like the order of evaluation is reversed.  That is, instead
>of translating to:
>
>    for r[col] in arr:
>        for col in range(len(arr[0])):
>            ...
>
>we actually have
>
>    for col in range(len(arr[0])):
>        for r[col] in arr:
>            ...

Nope.  See above.

>While I don't really understand it I may have some kind of rationale.
>Please let me know if this is correct.

You're thinking too much. :)

> The expression, itself, is also a LC.

This is the part that's causing you confusion.  This is a nested list comp:
for every iteration in the outer list comp, execute the expression.  The
fact that the expression happens to be a list comp itself just means that
the outer list comp will append a new list on each pass.

Wrap the inner list comp in a function call and it will make sense to you:

def innercomp(col):
    return [r[col] for r in arr]

[innercomp(col) for col in range(3)]

-> [[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]

(It's also significantly slower.)

>Well, hmmmmm.  OK.  Maybe I do understand it.  It just wasn't apparent
>at first.
>
>Am I close, or did I guess wrong?

You got it.

Note, however, that the same thing is far easier with zip():

>>> zip(*arr)
[(1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)]

If you need the items to be lists,

>>> [list(i) for i in zip(*arr)]
[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]

or,

>>> map(list, zip(*arr))
[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]

--
Francis Avila





More information about the Python-list mailing list