Python generator - ghdrako/doc_snipets GitHub Wiki
def perfect_squares(limit):
n = 1 while n <= limit:
yield n * n n += 1 squares_gen = perfect_squares(upper_limit) sum_gen = sum(squares_gen) assert sum_gen == sum_list == 333333833333500000
The perfect_squares function is a generator function. By calling this function with upper_limit, we’re creating a generator named squares_gen.
The most significant feature to observe is the yield keyword, which is the hallmark of a generator function. Whenever the operation executes to the yield line, it provides the item n * n. The coolest thing about a generator is the fact that it remembers which item it should yield next.
Every next(squares_gen) call reinstates the exe-cution of the generator, starting from where it was left: the line following the last yield execution. As the yield statement is part of the while loop, the loop runs con-tinuously, and each loop encounters the yield term once. When the loop is termi-nated, all the items are yielded, the generator is exhausted, and we’re done with the iteration.
REMINDER When you call next on iterators manually, you’ll encounter the StopIteration exception.
As an important conception, yield is different from return, which terminates the cur-rent execution and gives control back to the caller. By contrast, yield pauses the cur-rent execution and gives control back to the caller temporarily. When requested, it continues the execution.
The most important feature of a generator is that it renders an item when it’s asked to do so. Related to this feature is a computer programming concept called lazy evaluation, in which specific operations or variables aren’t evaluated until the need arises. In terms of generators, they don’t create all the items in the first place. Instead, a generator creates the next item only when it’s called on.
Generator is memory efficient:
squares_gen = perfect_squares(upper_limit)
print(squares_gen.__sizeof__())
The reason why it’s much smaller is that it needs to know only its current state; when it needs the next item, it can start from its current state and create the next item. By contrast, the list object needs to load all its items up front before it can use the
(expression for x in iterable)
>>> squares_gen_exp = (x * x for x in range(1, upper_limit))
>>> squares_gen_exp <generator object <genexpr> at 0x7f89a8111f50>
Instead of using the yield keyword in a generator function, a generator expression uses an expression directly to denote what the data should render.
>>> next(squares_gen_exp) 1
>>> next(squares_gen_exp) 4
>>> next(squares_gen_exp) 9
Let’s calculate the sum for the generator expression:
>>> sum_gen_exp = sum(squares_gen_exp) >>> sum_gen_exp 333332833333499986
The sum iswithout first 3 item switch co sumę by next
>>> sum(x*x for x in range(4))
14