/Demo/classes/Range.py

http://unladen-swallow.googlecode.com/ · Python · 93 lines · 85 code · 2 blank · 6 comment · 4 complexity · 939c442aee4a20ed0acfa19710cd46f8 MD5 · raw file

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