Does anyone know how send() works with generators when used recursively? I expected the value to be passed to the current generator, which could then pass it down to the recursed-generator...but it seems that is not the case? Some example code:
def Walk(obj):
recurse = (yield obj)
if not recurse:
print 'stop recurse:', recurse
return
if isinstance(obj, list):
print 'is list:', obj
for item in obj:
print 'item loop:', item
walker = Walk(item)
for x in walker:
print 'item walk:', x
recurse = (yield x)
print 'item walk recurse:', recurse
walker.send(recurse)
root = ['a', ['b.0', ['b.0.0']]]
walker = Walk(root)
for i, x in enumerate(walker):
print i, x
print 'send true'
walker.send(True)
The desired output is should be each value at each level recursion:
0 ['a', ['b.0', ['b.0.0']]]
1 'a'
2 ['b.0', ['b.0.0']]
3 'b.0'
4 ['b.0.0']
5 'b.0.0'
What ends up happening is:
0 ['a', ['b.0', ['b.0.0']]]
send true
is list: ['a', ['b.0', ['b.0.0']]]
item loop: a
item walk: a
item walk recurse: None
stop recurse: None
It looks like the inner-loop with recurse = (yield) doesn't wait for a value to be sent. Or something. Its not really clear how the inner-loop recurse value is getting None; its caller does call send().
Ultimately, the goal is basically to walk over a tree-structure recursively, but have the top-most caller be able to specify when not to recurse into a substructure. e.g.,
walker = Walk(root)
for node in walker:
if CriteriaMet(node):
walker.send(True)
else:
walker.send(False)
The important thing to realize is that send() also consumes!
From http://docs.python.org/reference/expressions.html#generator.send:
Resumes the execution and “sends” a value into the generator function. The value argument becomes the result of the current yield expression. The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value. When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value.
Here is a quick re-working of your code to get it to output as expected:
def Walk(obj):
recurse = (yield obj)
if not recurse:
#print 'stop recurse:', recurse
return
if isinstance(obj, list):
#print 'is list:', obj
for item in obj:
#print 'item loop:', item
walker = Walk(item)
recurse = None #first send must be None
while True:
try:
x = walker.send(recurse)
except StopIteration:
break
#print 'item walk:', x
recurse = (yield x)
#print 'item walk recurse:', recurse
root = ['a', ['b.0', ['b.0.0']]]
walker = Walk(root)
i = 0
x = walker.next()
while True:
print i, x
try:
x = walker.send(True)
except StopIteration:
break
i += 1
Output:
0 ['a', ['b.0', ['b.0.0']]]
1 a
2 ['b.0', ['b.0.0']]
3 b.0
4 ['b.0.0']
5 b.0.0
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