Kaydet (Commit) c2b151c6 authored tarafından Brett Cannon's avatar Brett Cannon

Add code for a range function that uses generators.

Cleaned up existing code by abstracting code to parse arguments.  Also removed
any unneeded operations (such as calling 'int' on a division when using floor
division also works).  Fixed a bug where the values  returned by
OldStyleRange could be short by one value.  Added more documentation.

Testing code also has a basic sanity check.
üst a6b3caad
# Example of a generator: re-implement the built-in range function """Example of a generator: re-implement the built-in range function
# without actually constructing the list of values. (It turns out without actually constructing the list of values.
# that the built-in function is about 20 times faster -- that's why
# it's built-in. :-)
OldStyleRange is coded in the way required to work in a 'for' loop before
iterators were introduced into the language; using __getitem__ and __len__ .
# Wrapper function to emulate the complicated range() arguments """
def handleargs(arglist):
"""Take list of arguments and extract/create proper start, stop, and step
values and return in a tuple"""
try:
if len(arglist) == 1:
return 0, int(arglist[0]), 1
elif len(arglist) == 2:
return int(arglist[0]), int(arglist[1]), 1
elif len(arglist) == 3:
if arglist[2] == 0:
raise ValueError("step argument must not be zero")
return tuple(int(x) for x in arglist)
else:
raise TypeError("range() accepts 1-3 arguments, given", len(arglist))
except TypeError:
raise TypeError("range() arguments must be numbers or strings "
"representing numbers")
def range(*a): def genrange(*a):
if len(a) == 1: """Function to implement 'range' as a generator"""
start, stop, step = 0, a[0], 1 start, stop, step = handleargs(a)
elif len(a) == 2: value = start
start, stop = a while value < stop:
step = 1 yield value
elif len(a) == 3: value += step
start, stop, step = a
else:
raise TypeError, 'range() needs 1-3 arguments'
return Range(start, stop, step)
class oldrange:
"""Class implementing a range object.
To the user the instances feel like immutable sequences
(and you can't concatenate or slice them)
# Class implementing a range object. Done using the old way (pre-iterators; __len__ and __getitem__) to have an
# To the user the instances feel like immutable sequences object be used by a 'for' loop.
# (and you can't concatenate or slice them)
class Range: """
# initialization -- should be called only by range() above def __init__(self, *a):
def __init__(self, start, stop, step): """ Initialize start, stop, and step values along with calculating the
if step == 0: nubmer of values (what __len__ will return) in the range"""
raise ValueError, 'range() called with zero step' self.start, self.stop, self.step = handleargs(a)
self.start = start self.len = max(0, (self.stop - self.start) // self.step)
self.stop = stop
self.step = step
self.len = max(0, int((self.stop - self.start) / self.step))
# implement repr(x) and is also used by print x
def __repr__(self): def __repr__(self):
"""implement repr(x) which is also used by print"""
return 'range(%r, %r, %r)' % (self.start, self.stop, self.step) return 'range(%r, %r, %r)' % (self.start, self.stop, self.step)
# implement len(x)
def __len__(self): def __len__(self):
"""implement len(x)"""
return self.len return self.len
# implement x[i]
def __getitem__(self, i): def __getitem__(self, i):
if 0 <= i < self.len: """implement x[i]"""
if 0 <= i <= self.len:
return self.start + self.step * i return self.start + self.step * i
else: else:
raise IndexError, 'range[i] index out of range' raise IndexError, 'range[i] index out of range'
# Small test program
def test(): def test():
import time, __builtin__ import time, __builtin__
print range(10), range(-10, 10), range(0, 10, 2) #Just a quick sanity check
for i in range(100, -100, -10): print i, correct_result = __builtin__.range(5, 100, 3)
print oldrange_result = list(oldrange(5, 100, 3))
genrange_result = list(genrange(5, 100, 3))
if genrange_result != correct_result or oldrange_result != correct_result:
raise Exception("error in implementation:\ncorrect = %s"
"\nold-style = %s\ngenerator = %s" %
(correct_result, oldrange_result, genrange_result))
print "Timings for range(1000):"
t1 = time.time() t1 = time.time()
for i in range(1000): for i in oldrange(1000):
pass pass
t2 = time.time() t2 = time.time()
for i in __builtin__.range(1000): for i in genrange(1000):
pass pass
t3 = time.time() t3 = time.time()
print t2-t1, 'sec (class)' for i in __builtin__.range(1000):
print t3-t2, 'sec (built-in)' pass
t4 = time.time()
print t2-t1, 'sec (old-style class)'
print t3-t2, 'sec (generator)'
print t4-t3, 'sec (built-in)'
test() if __name__ == '__main__':
test()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment