1"""Example of a generator: re-implement the built-in range function 2without actually constructing the list of values. 3 4OldStyleRange is coded in the way required to work in a 'for' loop before 5iterators were introduced into the language; using __getitem__ and __len__ . 6 7""" 8def handleargs(arglist): 9 """Take list of arguments and extract/create proper start, stop, and step 10 values and return in a tuple""" 11 try: 12 if len(arglist) == 1: 13 return 0, int(arglist[0]), 1 14 elif len(arglist) == 2: 15 return int(arglist[0]), int(arglist[1]), 1 16 elif len(arglist) == 3: 17 if arglist[2] == 0: 18 raise ValueError("step argument must not be zero") 19 return tuple(int(x) for x in arglist) 20 else: 21 raise TypeError("range() accepts 1-3 arguments, given", len(arglist)) 22 except TypeError: 23 raise TypeError("range() arguments must be numbers or strings " 24 "representing numbers") 25 26def genrange(*a): 27 """Function to implement 'range' as a generator""" 28 start, stop, step = handleargs(a) 29 value = start 30 while value < stop: 31 yield value 32 value += step 33 34class oldrange: 35 """Class implementing a range object. 36 To the user the instances feel like immutable sequences 37 (and you can't concatenate or slice them) 38 39 Done using the old way (pre-iterators; __len__ and __getitem__) to have an 40 object be used by a 'for' loop. 41 42 """ 43 44 def __init__(self, *a): 45 """ Initialize start, stop, and step values along with calculating the 46 nubmer of values (what __len__ will return) in the range""" 47 self.start, self.stop, self.step = handleargs(a) 48 self.len = max(0, (self.stop - self.start) // self.step) 49 50 def __repr__(self): 51 """implement repr(x) which is also used by print""" 52 return 'range(%r, %r, %r)' % (self.start, self.stop, self.step) 53 54 def __len__(self): 55 """implement len(x)""" 56 return self.len 57 58 def __getitem__(self, i): 59 """implement x[i]""" 60 if 0 <= i <= self.len: 61 return self.start + self.step * i 62 else: 63 raise IndexError, 'range[i] index out of range' 64 65 66def test(): 67 import time, __builtin__ 68 #Just a quick sanity check 69 correct_result = __builtin__.range(5, 100, 3) 70 oldrange_result = list(oldrange(5, 100, 3)) 71 genrange_result = list(genrange(5, 100, 3)) 72 if genrange_result != correct_result or oldrange_result != correct_result: 73 raise Exception("error in implementation:\ncorrect = %s" 74 "\nold-style = %s\ngenerator = %s" % 75 (correct_result, oldrange_result, genrange_result)) 76 print "Timings for range(1000):" 77 t1 = time.time() 78 for i in oldrange(1000): 79 pass 80 t2 = time.time() 81 for i in genrange(1000): 82 pass 83 t3 = time.time() 84 for i in __builtin__.range(1000): 85 pass 86 t4 = time.time() 87 print t2-t1, 'sec (old-style class)' 88 print t3-t2, 'sec (generator)' 89 print t4-t3, 'sec (built-in)' 90 91 92if __name__ == '__main__': 93 test() 94