2

I am trying to create the analogous operation of transposing a list of lists.

Here is the function using standard list comprehension:

matrix = [[(i, j) for j in range(3)] for i in range(3)]
for row in zip(*matrix):
    print(list(row))
#
[(0, 0), (1, 0), (2, 0)]
[(0, 1), (1, 1), (2, 1)]
[(0, 2), (1, 2), (2, 2)]
#

While the analogous expression, using generator expressions

generators = (((i, j) for j in range(3)) for i in range(3))
for generator in zip(*generators):
    print(list(generator))
#
[(2, 0), (2, 0), (2, 0)]
[(2, 1), (2, 1), (2, 1)]
[(2, 2), (2, 2), (2, 2)]
#

Just copies the last generator repeatedly.

As an intermediate step, this yields a proper transpose

generators = ([(i, j) for j in range(3)] for i in range(3))
user18764
  • 253
  • 2
  • 10
  • What happens is basically that `i` in each generator is bound to the outer `i` and not being *"saved"* into each generator. When doing `*generators` you **exhaust** the outer generator and not iterating it. This brings `i` to be equal to `2` at the end. As all 3 inner generators **share the same `i`**, you get the result you're seeing – Tomerikoo Jun 02 '20 at 19:46
  • Yeah I was starting to grok that once I realized these were identical/similar to closures. – user18764 Jun 02 '20 at 20:01
  • exactly. You can get passed that by actually defining a generator with `def` and using it instead of the `((i, j) for j in range(3))`. Like: `def counter(i): yield from ((i, j) for j in range(3))`. This will create the necessary closure over `i` – Tomerikoo Jun 02 '20 at 20:05

0 Answers0