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