[Edu-sig] Example Generator (2.2a)

Kirby Urner pdx4d@teleport.com
Sun, 22 Jul 2001 23:42:37 -0700


Here's some quick results of my play with the 2.2a
generator feature.

In the module below, primes() is defined not with 'return'
but with keyword 'yield', signifying this is a special
kind of function known as a generator.  It preserves state
between calls, picking up execution right after the yield.
No need for threads.

What play() returns, on being called, is a generator object:

   >>> from play import primes
   >>> p = primes()
   >>> p
   <generator object at 0139EA0C>
   >>> p.__class__
   <type 'generator'>
   >>> dir(p.__class__)
   ['__class__', '__getattr__', '__init__', '__iter__', '__new__',
   'gi_frame', 'gi_running', 'next']
   >>> p.__class__.__base__
   <type 'object'>

Notice that a generator, like an iterator, implements a
next() method.  You can use next() explicitly to trigger
successive yields, or implicitly by iterating over your
generator object in a for-loop:

   >>> [p.next() for i in range(30)]
   [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
   53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107,
   109, 113]

   >>> for i in p:
   	  print i,
	  if i>100:  break

   127 131 137 139 149 151 157 163 167 173 179 181 191 193
   197 199 211 223 227 229 233 239 241 251

Notice how the 2nd use of p, in a for-loop, continues spitting
out primes where the first list comprehension loop left off
-- because p was not reinitialized in between, and therefore
keeps going on up the list of primes.

This is an example of lazy evaluation.  We don't do the work
to get a next prime until we need it, and we can treat the
generator object as a source of primes.

Note that even though primes() is maintaining state info
between calls, you can't access its local variables as
attributes as you might for a class, e.g. p.pr is not defined.
This is why, if I want to make my growing list of primes
available as a persistent list object, I needed to make
it global.

Kirby

==============
"""
play.py  -- fun with generators
"""

from __future__ import generators

def primes():
     """
     Generator:
     Yield next higher prime, starting at 2, accruing
     successive primes in global list pr
     """
     global pr
     pr = [2]
     i,new = 1
     while 1:
         i += 2
         if new:
             yield pr[-1]
             new = 0
         for t in pr:
             if t*t > i:
                 # if t**2>candidate, we have a new prime
                 pr.append(i)
                 new = 1
                 break
             if i%t == 0:
                 # test divide by primes so far,
                 # move to next odd whenever remainder=0
                 break