etuardu
etuardu

Reputation: 5536

python3: list() from generator: strange behaviour when yielding item-changed lists

I have a generator defined like this:

def gen():
    r = [0]
    yield r
    r[0] = 1
    yield r
    r[0] = 2
    yield r

it will yield three lists of one element going from 0 to 2:

>>> a = gen()
>>> next(a)
[0]
>>> next(a)
[1]
>>> next(a)
[2]
>>> next(a)
Traceback (most recent call last):
  File "<pyshell#313>", line 1, in <module>
    next(a)
StopIteration

Now, when I go to make a list from the generator, I got this:

>>> list(gen())
[[2], [2], [2]]

That is, it seems to yield each time the very last computed value.

Is this a python bug or am I missing something?

Upvotes: 1

Views: 431

Answers (2)

Lennart Regebro
Lennart Regebro

Reputation: 172377

You want:

def gen():
    for i in (0,1,2):
        yield [i]

That will yield three lists, not one list three times.

Upvotes: 0

user395760
user395760

Reputation:

It's not a bug, it does exactly what you told it to do. You're yielding the very same object several times, so you get several references to that object. The only reason you don't see three [2]s in your first snippet is that Python won't go back in time and change previous output to match when objects are mutated. Try storing the values you get when calling next explicitly in variables and check them at the end - you'll get the same result.

Such an iterator is only useful if no yielded value is used after the iterator is advanced another time. Therefore I'd generally avoid it, as it produces unexpected results when trying to pre-compute some or all results (this also means it breaks various useful tricks such as itertools.tee and iterable unpacking).

Upvotes: 2

Related Questions