14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""Example of a generator: re-implement the built-in range function
24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmwithout actually constructing the list of values.
34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmOldStyleRange is coded in the way required to work in a 'for' loop before
54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmiterators were introduced into the language; using __getitem__ and __len__ .
64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""
84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef handleargs(arglist):
94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """Take list of arguments and extract/create proper start, stop, and step
104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    values and return in a tuple"""
114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    try:
124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if len(arglist) == 1:
134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return 0, int(arglist[0]), 1
144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif len(arglist) == 2:
154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return int(arglist[0]), int(arglist[1]), 1
164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif len(arglist) == 3:
174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if arglist[2] == 0:
184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                raise ValueError("step argument must not be zero")
194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return tuple(int(x) for x in arglist)
204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise TypeError("range() accepts 1-3 arguments, given", len(arglist))
224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    except TypeError:
234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise TypeError("range() arguments must be numbers or strings "
244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        "representing numbers")
254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef genrange(*a):
274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """Function to implement 'range' as a generator"""
284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    start, stop, step = handleargs(a)
294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    value = start
304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    while value < stop:
314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        yield value
324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        value += step
334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass oldrange:
354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """Class implementing a range object.
364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    To the user the instances feel like immutable sequences
374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    (and you can't concatenate or slice them)
384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Done using the old way (pre-iterators; __len__ and __getitem__) to have an
404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    object be used by a 'for' loop.
414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """
434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, *a):
454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """ Initialize start, stop, and step values along with calculating the
464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        nubmer of values (what __len__ will return) in the range"""
474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.start, self.stop, self.step = handleargs(a)
484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.len = max(0, (self.stop - self.start) // self.step)
494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __repr__(self):
514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """implement repr(x) which is also used by print"""
524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return 'range(%r, %r, %r)' % (self.start, self.stop, self.step)
534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __len__(self):
554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """implement len(x)"""
564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.len
574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __getitem__(self, i):
594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """implement x[i]"""
604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if 0 <= i <= self.len:
614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return self.start + self.step * i
624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise IndexError, 'range[i] index out of range'
644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef test():
674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    import time, __builtin__
684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    #Just a quick sanity check
694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    correct_result = __builtin__.range(5, 100, 3)
704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    oldrange_result = list(oldrange(5, 100, 3))
714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    genrange_result = list(genrange(5, 100, 3))
724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if genrange_result != correct_result or oldrange_result != correct_result:
734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise Exception("error in implementation:\ncorrect   = %s"
744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                         "\nold-style = %s\ngenerator = %s" %
754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                         (correct_result, oldrange_result, genrange_result))
764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print "Timings for range(1000):"
774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    t1 = time.time()
784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    for i in oldrange(1000):
794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        pass
804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    t2 = time.time()
814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    for i in genrange(1000):
824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        pass
834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    t3 = time.time()
844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    for i in __builtin__.range(1000):
854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        pass
864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    t4 = time.time()
874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print t2-t1, 'sec (old-style class)'
884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print t3-t2, 'sec (generator)'
894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print t4-t3, 'sec (built-in)'
904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmif __name__ == '__main__':
934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    test()
94