When I append two generators to a list using a loop the first generator duplicates the second generator's output. When I unroll the loop I get different output as I expected.
The following code demonstrates the issue.
import itertools
iterators = itertools.tee(itertools.repeat(('a', 0), 5), 2)
result = []
result.append(r[0] for r in iterators[0])
result.append(r[1] for r in iterators[1])
# As expected
print('Written out...')
print(list(result[0])) # ['a', 'a', 'a', 'a', 'a']
print(list(result[1])) # [0, 0, 0, 0, 0]
# Now do it again but use a loop
iterators = itertools.tee(itertools.repeat(('a', 0), 5), 2)
result = []
for index in [0, 1]:
result.append(r[index] for r in iterators[index])
# This time both lists are of the second item.
print('With a loop...')
print(list(result[0])) # [0, 0, 0, 0, 0] <--- Huh?!
print(list(result[1])) # [0, 0, 0, 0, 0]
Why does the loop version not work as I expected? What can I do about it?
Solution
Now this is closed as a duplicate I can't post another answer, but for the record, here is the solution I used finally.
The problem as pointed out by @MikeMüller is that the instance of index
that indexes r
is late-bound. The following forces early-binding by making a new local variable instance i
for each value of index
in the loop:
for index, it in enumerate(iterators):
g = lambda i: (r[i] for r in it) # force early binding on index
result.append(g(index))
(I also liked Mike's suggestion to use generators all the way down, but unfortuantely I need the outer generator (for result
) to be materialised so I can refer repeatedly to the individual elements in result
. But list(result)
has the same behaviour as my original loop code.)