# Generator oddity

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Fri May 1 14:48:18 CEST 2009

```On Fri, 01 May 2009 04:58:16 -0700, opstad wrote:

> I'm a little baffled by the inconsistency here. Anyone have any
> explanations?
>
>>>> def gen():
> ...   yield 'a'
> ...   yield 'b'
> ...   yield 'c'
> ...
>>>> [c1 + c2 for c1 in gen() for c2 in gen()]
> ['aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc']
>
>>>> list(c1 + c2 for c1 in gen() for c2 in gen())
> ['aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc']
>
>>>> it1 = gen()
>>>> it2 = gen()
>>>> list(c1 + c2 for c1 in it1 for c2 in it2)
> ['aa', 'ab', 'ac']
>
> Why does this last list only have three elements instead of nine?

Ah, good one! That had me puzzled for a while too.

The answer is to write it out as a nested for-loop, using print in place
of yield. Here's the first way:

for c1 in gen():
for c2 in gen():
print c1 + c2

And the second:

it1 = gen()
it2 = gen()
for c1 in it1:
for c2 in it2:
print c1 + c2

In the first example, the inner loop gets refreshed each time through the
outer loop with a brand new instance of the generator. Expanding the
loops in full:

# First method:
c1 = 'a'
call gen() to make an iterable
step through the fresh iterable, giving 'aa' 'ab' 'ac'
c1 = 'b'
call gen() to make an iterable
step through the fresh iterable, giving 'ba' 'bb' 'bc'
c1 = 'c'
call gen() to make an iterable
step through the fresh iterable, giving 'ca' 'cb' 'cc'

# Second method:
c1 = 'a'
step through the iterable, giving 'aa' 'ab' 'ac'
c1 = 'b'
inner iterable is exhausted, so do nothing
c1 = 'c'
inner iterable is exhausted, so do nothing

And there you have it. A nice Gotcha for the books.

--
Steven

```