| 1 | """Example of a generator: re-implement the built-in range function
|
|---|
| 2 | without actually constructing the list of values.
|
|---|
| 3 |
|
|---|
| 4 | OldStyleRange is coded in the way required to work in a 'for' loop before
|
|---|
| 5 | iterators were introduced into the language; using __getitem__ and __len__ .
|
|---|
| 6 |
|
|---|
| 7 | """
|
|---|
| 8 | def 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 |
|
|---|
| 26 | def 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 |
|
|---|
| 34 | class 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 |
|
|---|
| 66 | def 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 |
|
|---|
| 92 | if __name__ == '__main__':
|
|---|
| 93 | test()
|
|---|