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