Python language basics 65: keeping state in yield generators
November 21, 2015 Leave a comment
In the previous post we looked at a basic example of using the yield keyword in Python. We saw that yield generator functions are essentially collections that return a certain number of elements when they are enumerated. The elements to be returned – yielded – are not loaded into memory before they are required in a loop. We also said that generator functions can “remember” the state of their variables.
We’ll see an example of what this means in code.
What is meant by “state“? If you are new to programming then this may sound like some mysterious and complex term but it’s quite simple really. The state of an object means what properties the object has “here and now”. E.g. if x is assigned the value of 3 then that is its current state. Later on if its value is reassigned to 10 then the state of x has been changed.
Objects can have multiple properties of course. A house can have an address, the number of rooms, storeys etc. All these properties together make up the state of the object at runtime. You might also hear the expression inconsistent state or invalid state. An object or variable can be in an invalid state if one or more of its properties violates some rule. E.g. normally a price cannot be negative. If the variable x represents a price and it becomes negative due to some error in the code then we say that x is in an inconsistent or invalid state.
Yield functions “remember” the state of their internal variables after each iteration. Consider the following bit of code:
def integer_yielder(): print("In yield function at counter 100") counter = 100 print("Counter: ", counter) yield 1 print("In yield function at counter + 10") counter += 10 print("Counter: ", counter) yield 2 print("In yield function at counter + 20") counter += 20 print("Counter: ", counter) yield 3 print("In yield function at counter + 30") counter += 30 print("Counter: ", counter) yield 4 print("In yield function at counter + 40") counter += 40 print("Counter: ", counter) integers = integer_yielder() for i in integers: print("In yielder calling function, printing ", i)
Here’s the output of the code:
In yield function at counter 100
In yielder calling function, printing 1
In yield function at counter + 10
In yielder calling function, printing 2
In yield function at counter + 20
In yielder calling function, printing 3
In yield function at counter + 30
In yielder calling function, printing 4
In yield function at counter + 40
The print statements will show you in what order each line of code was executed. If integer_yielder were a normal function then all of its statements would be printed at one go. However, here we see that code execution jumps in and out of the generator function and the latest state of the variable counter is saved.
In the next post we’ll look at a practical implementation of yield generators.
Read all Python-related posts on this blog here.