You have a nested list comprehension, an outer list comprehension creating a new list for every iteration.
You can deconstruct any list comprehension by reading the for
and if
components from left to right as nested blocks. Everything before the first for
is the expression that produces the elements of the list.
So the basic structure is:
[<expression> for <targets> in <iterable> <optionally more if and for>]
which can always be converted to a regular loop with:
_result = []
for <targets> in <iterable>:
<optionally more if and for blocks, add a new level each time>
_result.append(<expression>)
I added in _result
as an explicit name for the list that is produced; note that the result of <expression>
is appended to it each iteration.
Your example breaks down to two list comprehensions. The outer loop is this:
[[m[i] for m in a] for i in range(cols)]
# \ / | \ /
# <expression> <targets> <iterable>
So the [m[i] for m in a]
is the expression here! Writing this out that becomes:
_result_outer = []
for i in range(cols):
_result_outer.append([m[i] for m in a])
The inner list comprehension is straightforward:
_result_inner = []
for m in a:
_result_inner.append(m[i])
If you put these two together you get:
_result_outer = []
for i in range(cols):
_result_inner = []
for m in a:
_result_inner.append(m[i])
_result_outer.append(_result_inner)
Now you can see that the i
target from the outer loop, is used each time in the inner loop. Because i
loops over the number of columns, but m
is a row, this produces first a list with all the values from the very first column, then the second column, and then the third, etc.
The line before the list comprehension reversed the rows:
a = a[::-1]
This takes all rows, and goes from the last to the first (stepping backwards, with a -1
step size). This makes it so that the list comprehension loops over the rows in reverse order.
The code can be cleaned up and improved a little. The rows
variable is never used, for example, and we can use the reversed()
function to loop over a list in reverse order without creating a copy:
def rotateImage(a):
cols = len(a[0])
return [[m[i] for m in reversed(a)] for i in range(cols)]
Another method of transposing a matrix (moving columns to rows) is using the zip()
function with all the rows applied as separate arguments, with the *<expression>
call syntax:
def rotateImage(a):
return [list(r) for r in zip(*reversed(a))]
This still uses a list comprehension, because zip()
produces an iterable object, not a list, and each time you iterate you get a tuple. The above list comprehension converts the whole result back to a list of lists.
If you want to do this in place, I suggest you read the very excellent answer Jack wrote on rotating a matrix with Python; their explanation of how to code this is second to none: How do you rotate a two dimensional array?