Reputation: 12462
21 def power(values):
22 print "power", values
23 for value in values:
24 print 'powering %s' % value
25 yield value
26
27 def adder(values):
28 print "adder", values
29 for value in values:
30 print 'adding to %s' % value
31 if value % 2 ==0:
32 yield value + 3
33 else:
34 yield value + 2
35
36 elements = [1, 4, 7, 9, 12, 19]
37 res = adder(power(elements))
38 print res.next()
39 print res.next()
40 print res.next()
41 print res.next()
OUTPUT:
adder <generator object power at 0x7fb6b9ee7910> <--- is this the stdout flush matter?
power [1, 4, 7, 9, 12, 19]
powering 1
adding to 1
3
powering 4
adding to 4
7
powering 7
adding to 7
9
powering 9
adding to 9
11
I am trying to understand the above code.
1) why did adder got printed before power [1, 4, 7, 9, 12, 19]?
2) adder is not iterating through elements but iterating through power generator, correct?
3) confirm my thought on (1). so adder gets called first then in for value in values
for adder, it's consulting power generator and hence, powering print line gets triggered and then adder print line gets triggered?
4) If so, why doesnt the print statement power [1, 4, 7, 9, 12, 19]
gets called along with the powering <$>
print statement every time?
Upvotes: 2
Views: 1118
Reputation: 39406
power
has a yield
return, which makes it a generator
. The code in the function itself is only execute when next()
is called on it.adder
relies on the generator, and has no information on the data being iterated. (such as the size)yield
is a particular instruction. It does not return from the generator function (power
). Instead, it provides one value, and suspend its execution until the next call to next()
. At this point, the execution is resumed at the same point, which is inside the loop.Illustration of the yield
stopping point:
def gene():
print 'first!'
yield 1
print 'second!'
yield 2
g = gene()
g.next()
# first!
# 1
g.next()
# second!
# 2
As you can see, the generator is interrupted exactly after the yield
instruction, ready to execute the next one
Upvotes: 4
Reputation: 208545
Generator functions don't begin execution until the first call to __next__
on the generator. For example:
>>> def gen():
... print 'starting execution'
... for i in range(10): yield i
...
>>> itr = gen()
>>> next(itr)
starting execution
0
So to answer you first question, "why did adder got printed before power", when you do adder(power(elements))
the generator power(elements)
is created first, but the execution of that generator will not begin until the first iteration of for value in values
within adder()
.
Upvotes: 4
Reputation: 375675
Code inside a generator is not run until next is called:
def gen():
print "called"
yield 3.14
g = gen() # nothing is printed
g.next() # print "called" and return 3.14
The for loop is doing the job of calling next here, in your code this is happening after the adder is printed:
g = gen()
print 'adder' # prints adder
for i in g: # prints called (as inside generator, *before* yields)
print i # prints 3.14
Upvotes: 3