I found this example and I can't understand why it works unpredictably? I supposed it must output [1, 8, 15]
or [2, 8, 22]
.
array = [1, 8, 15] g = (x for x in array if array.count(x) > 0) array = [2, 8, 22] print(list(g)) >>>[8]
In terms of syntax, a generator expression uses square brackets [] while a list comprehension uses parentheses () .
So what's the difference between Generator Expressions and List Comprehensions? The generator yields one item at a time and generates item only when in demand. Whereas, in a list comprehension, Python reserves memory for the whole list. Thus we can say that the generator expressions are memory efficient than the lists.
List comprehensions and generators are not different at all; they are just different ways of writing the same thing. A list comprehension produces a list as output, a generator produces a generator object.
Python Generator functions allow you to declare a function that behaves likes an iterator, allowing programmers to make an iterator in a fast, easy, and clean way. An iterator is an object that can be iterated or looped upon. It is used to abstract a container of data to make it behave like an iterable object.
What are Generator Expressions? Generator Expressions are somewhat similar to list comprehensions, but the former doesn’t construct list object. Instead of creating a list and keeping the whole sequence in the memory, the generator generates the next element in demand.
Generator Expressions are somewhat similar to list comprehensions, but the former doesn’t construct list object. Instead of creating a list and keeping the whole sequence in the memory, the generator generates the next element in demand.
There are various other expressions that can be simply coded similar to list comprehensions but instead of brackets we use parenthesis. These expressions are designed for situations where the generator is used right away by an enclosing function. Generator expression allows creating a generator without a yield keyword.
After much discussion, it was decided that the first (outermost) for-expression [of the generator expression] should be evaluated immediately and that the remaining expressions be evaluated when the generator is executed. [...] Python takes a late binding approach to lambda expressions and has no precedent for automatic, early binding.
The reason is that, at creation time, the generator (a for b in c if d)
only evaluates c
(which sometimes makes b
predictable as well). But a
, b
, d
are evaluated at consumption time (at each iteration). Here, it uses the current binding of array
from the enclosing scope when evaluating d
(array.count(x) > 0
).
You can for instance do:
g = (x for x in [] if a)
Without having declared a
in advance. But, you have to make sure a
exists when the generator is consumed.
But you cannot do similarly:
g = (x for x in a if True)
Upon request:
You can observe similar (however not identical) patterns with a common generator function:
def yielder(): for x in array: if array.count(x) > 0: yield x array = [1, 8, 15] y = yielder() array = [2, 8, 22] list(y) # [2, 8, 22]
The generator function does not execute any of its body ahead of consumption. Hence, even the array
in the for-loop header is bound late. An even more disturbing example occurs where we "switch out" array
during iteration:
array = [1, 8, 15] y = yielder() next(y) # 1 array = [3, 7] next(y) # still iterating [1, 8, 15], but evaluating condition on [3, 7] # StopIteration raised
From the docs on Generator expressions:
Variables used in the generator expression are evaluated lazily when the
__next__()
method is called for the generator object (in the same fashion as normal generators). However, the iterable expression in the leftmostfor
clause is immediately evaluated, so that an error produced by it will be emitted at the point where the generator expression is defined, rather than at the point where the first value is retrieved.
So when you run
array = [1, 8, 15] g = (x for x in array if array.count(x) > 0)
only the first array
in the generator expression is evaluated. x
and array.count(x)
will only be evaluated when you call next(g)
. Since you make array
point to another list [2, 8, 22]
before consuming the generator you get the 'unexpected' result.
array = [2, 8, 22] print(list(g)) # [8]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With