Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 1 | """Example of a generator: re-implement the built-in range function |
| 2 | without actually constructing the list of values. |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 3 | |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 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__ . |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 6 | |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 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") |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 25 | |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 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 |
Andrew M. Kuchling | 946c53e | 2003-04-24 17:13:18 +0000 | [diff] [blame] | 33 | |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 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) |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 38 | |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 39 | Done using the old way (pre-iterators; __len__ and __getitem__) to have an |
| 40 | object be used by a 'for' loop. |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 41 | |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 42 | """ |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 43 | |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 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) |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 49 | |
Andrew M. Kuchling | 946c53e | 2003-04-24 17:13:18 +0000 | [diff] [blame] | 50 | def __repr__(self): |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 51 | """implement repr(x) which is also used by print""" |
Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 52 | return 'range(%r, %r, %r)' % (self.start, self.stop, self.step) |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 53 | |
Andrew M. Kuchling | 946c53e | 2003-04-24 17:13:18 +0000 | [diff] [blame] | 54 | def __len__(self): |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 55 | """implement len(x)""" |
Andrew M. Kuchling | 946c53e | 2003-04-24 17:13:18 +0000 | [diff] [blame] | 56 | return self.len |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 57 | |
Andrew M. Kuchling | 946c53e | 2003-04-24 17:13:18 +0000 | [diff] [blame] | 58 | def __getitem__(self, i): |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 59 | """implement x[i]""" |
| 60 | if 0 <= i <= self.len: |
Andrew M. Kuchling | 946c53e | 2003-04-24 17:13:18 +0000 | [diff] [blame] | 61 | return self.start + self.step * i |
| 62 | else: |
Collin Winter | 6f2df4d | 2007-07-17 20:59:35 +0000 | [diff] [blame] | 63 | raise IndexError('range[i] index out of range') |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 64 | |
| 65 | |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 66 | def test(): |
Georg Brandl | 1a3284e | 2007-12-02 09:40:06 +0000 | [diff] [blame] | 67 | import time, builtins |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 68 | #Just a quick sanity check |
Alexander Belopolsky | 7cb6f2f | 2010-07-04 17:47:30 +0000 | [diff] [blame] | 69 | correct_result = list(builtins.range(5, 100, 3)) |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 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)) |
Collin Winter | 6f2df4d | 2007-07-17 20:59:35 +0000 | [diff] [blame] | 76 | print("Timings for range(1000):") |
Andrew M. Kuchling | 946c53e | 2003-04-24 17:13:18 +0000 | [diff] [blame] | 77 | t1 = time.time() |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 78 | for i in oldrange(1000): |
Andrew M. Kuchling | 946c53e | 2003-04-24 17:13:18 +0000 | [diff] [blame] | 79 | pass |
| 80 | t2 = time.time() |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 81 | for i in genrange(1000): |
Andrew M. Kuchling | 946c53e | 2003-04-24 17:13:18 +0000 | [diff] [blame] | 82 | pass |
| 83 | t3 = time.time() |
Georg Brandl | 1a3284e | 2007-12-02 09:40:06 +0000 | [diff] [blame] | 84 | for i in builtins.range(1000): |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 85 | pass |
| 86 | t4 = time.time() |
Collin Winter | 6f2df4d | 2007-07-17 20:59:35 +0000 | [diff] [blame] | 87 | print(t2-t1, 'sec (old-style class)') |
| 88 | print(t3-t2, 'sec (generator)') |
| 89 | print(t4-t3, 'sec (built-in)') |
Guido van Rossum | e876949 | 1992-08-13 12:14:11 +0000 | [diff] [blame] | 90 | |
| 91 | |
Brett Cannon | c2b151c | 2004-06-27 23:17:35 +0000 | [diff] [blame] | 92 | if __name__ == '__main__': |
| 93 | test() |