Question

 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?

Was it helpful?

Solution

  1. 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.
  2. correct. adder relies on the generator, and has no information on the data being iterated. (such as the size)
  3. correct again
  4. 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.

Edit

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

OTHER TIPS

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().

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top