# [Tutor] generators and classes

Gregor Lingl glingl at aon.at
Tue Feb 24 18:46:46 EST 2004

Marilyn Davis schrieb:

>Thank you Lloyd.
>
>On Tue, 24 Feb 2004, Lloyd Kvam wrote:
>
>
>
>>Look at SimPy for some generator code with classes that would be hard to
>>do otherwise.
>>
>>http://simpy.sourceforge.net/
>>Home
>>
>>
>
>I can't take on such a big study right now.
>
>I did do 2 other implementations, ...
>
Another one, just as a contribution to the disussion:

class NumberHat:
def __init__(self, domain):
self.domain = domain
def gen(self, values):
while values:
index = random.randrange(len(values))
yield values.pop(index)
def __iter__(self):
return self.gen(self.domain)

>>> hat = NumberHat(range(10))
>>> [x for x in hat]
[4, 2, 0, 9, 7, 5, 6, 1, 3, 8]
>>> [x for x in hat]
[]
>>> [x for x in NumberHat(range(-3,4))]
[2, -1, 1, 3, -2, -3, 0]

BTW, I observed, that if you replace the
next() method in

>#! /usr/bin/env python2.2
>
>import random
>
>class NumberHat:
>    '''Generator to deliver randomly chosen values from bot
>to over_top - 1, delivering each value once.  This one
>implements an __iter__ method so that you can use a next()
>call.'''
>
>    def __init__(self, bot, over_top):
>        ...
>
>
>    def __iter__(self):
>        return self
>
>    def next(self):
>        self.at += 1
>        return self.numbers[self.at]
>
>
by:

def next(self):
self.at += 1
try:
return self.numbers[self.at]
except:
raise StopIteration

you get a full fledged generator and the
[x for x in hat] idiom works also with this class.

Finally: in the Python docs you find:

You could achieve the effect of generators manually by writing your own
class and storing all the local variables of the generator as instance
variables. For example, returning a list of integers could be done by
setting |self.count| to 0, and having the next() method increment
|self.count| and return it. However, for a moderately complicated
generator, writing a corresponding class would be much messier.
Lib/test/test_generators.py contains a number of more interesting examples.

So I'd prefer to stick with an ordinary generator function like
your unique(), but avoiding to get stuck in an infinite loop,
because no more unused randomnumber can be found, e.g.:

def unique(bot, over_top):
domain = range(bot,over_top)
while domain:
index = random.randrange(len(domain))
yield domain.pop(index)

Regards, Gregor