Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does redefining a variable used in a generator give strange results? [duplicate]

One of my friends asked me about this piece of code:

array = [1, 8, 15]
gen = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]
print(list(gen))

The output:

[8]

Where did the other elements go?

like image 756
pickle rick Avatar asked Oct 21 '21 10:10

pickle rick


People also ask

What is generator expression in Python?

A generator expression is an expression that returns a generator object. Basically, a generator function is a function that contains a yield statement and returns a generator object.

How do generators work Python?

A Python generator is a function that produces a sequence of results. It works by maintaining its local state, so that the function can resume again exactly where it left off when called subsequent times. Thus, you can think of a generator as something like a powerful iterator.

What is the value in using a generator in Python?

Python provides a generator to create your own iterator function. A generator is a special type of function which does not return a single value, instead, it returns an iterator object with a sequence of values. In a generator function, a yield statement is used rather than a return statement.

How do you access the generator object in Python?

You need to call next() or loop through the generator object to access the values produced by the generator expression. When there isn't the next value in the generator object, a StopIteration exception is thrown. A for loop can be used to iterate the generator object.


Video Answer


2 Answers

The answer is in the PEP of the generator expressions, in particular the session Early Binding vs Late biding:

After much discussion, it was decided that the first (outermost) for-expression should be evaluated immediately and that the remaining expressions be evaluated when the generator is executed.

So basically the array in:

x for x in array 

is evaluated using the original list [1, 8, 15] (i.e. immediately), while the other one:

if array.count(x) > 0

is evaluated when the generator is executed using:

print(list(gen))

at which point array refers to a new list [2, 8, 22]

like image 173
Dani Mesejo Avatar answered Oct 14 '22 03:10

Dani Mesejo


This becomes more clear if you give each array a unique name instead of re-binding array:

array1 = [1, 8, 15]
gen = (x for x in array1 if array2.count(x) > 0)
array2 = [2, 8, 22]
print(list(gen))

x for x in array1 is evaluated at creation of the generator, but if array2.count(x) > 0 is evaluated lazily, which is why you can already reference a yet undefined variable

like image 27
Felk Avatar answered Oct 14 '22 03:10

Felk