First case -- Simple Generator
The generator expression (x*x for x in range(3))
is roughly the same as the simple generator function you described. However, scoping for the genexp can be a little more complicated (which is why we usually recommend that you consume generator expressions immediately rather than passing them around).
Second Case -- Enhanced Generators
The code with the y = yield x
is an example of an enhanced generator which is used to send data into a running generator, essentially creating a two-way communication channel between the running generator and the calling code.
Principal use cases for the send/receive logic are to implement coroutines and generator trampolines. See this trampoline example from David Beazley.
Enhanced generators are the key to Twisted Python's beautiful Inline Callbacks which implement coroutines.
How to Examine the Generator
For the variable y in y = func()
, the only inspection technique is to examine the public API:
>>> y = func()
>>> dir(y)
['__class__', '__delattr__', '__doc__', '__format__',
'__getattribute__', '__hash__', '__init__', '__iter__',
'__name__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'close', 'gi_code', 'gi_frame',
'gi_running', 'next', 'send', 'throw']
How to Inspect the Generator Function
For the generator function itself, you can use the dis module to inspect the code to see how it works:
>>> def func():
x = 1
while 1:
y = yield x
x += y
>>> import dis
>>> dis.dis(func)
3 0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (x)
4 6 SETUP_LOOP 21 (to 30)
5 >> 9 LOAD_FAST 0 (x)
12 YIELD_VALUE
13 STORE_FAST 1 (y)
6 16 LOAD_FAST 0 (x)
19 LOAD_FAST 1 (y)
22 INPLACE_ADD
23 STORE_FAST 0 (x)
26 JUMP_ABSOLUTE 9
29 POP_BLOCK
>> 30 LOAD_CONST 0 (None)
33 RETURN_VALUE
Tracing the Code with a Debugger
You can use the pdb debugger to trace through the code step-by-step.
>>> import pdb
>>> y = func()
>>> pdb.runcall(next, y)
> /Users/raymond/Documents/tmp.py(2)func()
-> x = 1
(Pdb) s
> /Users/raymond/Documents/tmp.py(3)func()
-> while 1:
(Pdb) s
> /Users/raymond/Documents/tmp.py(4)func()
-> y = yield x
(Pdb) p locals()
{'x': 1}
(Pdb) s
> /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/bdb.py(440)runcall()
-> self.quitting = 1
(Pdb) s
1
>>> pdb.runcall(y.send, 10)
> /Users/raymond/Documents/tmp.py(5)func()->1
-> x += y
(Pdb) s
> /Users/raymond/Documents/tmp.py(4)func()->1
-> y = yield x
(Pdb) locals()
{'__return__': 1, 'x': 11, 'y': 10}