Skip to content

Commit 7fe5264

Browse files
committed
Merge pull request #130 from bradmwalker/more-range-work
newrange: read-only getters, empty range slice fix, and refactoring
2 parents ae772c3 + 2b09682 commit 7fe5264

File tree

2 files changed

+46
-29
lines changed

2 files changed

+46
-29
lines changed

src/future/types/newrange.py

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"""
2020

2121
from collections import Sequence, Iterator
22+
from itertools import islice
2223

2324

2425
class newrange(Sequence):
@@ -55,6 +56,18 @@ def __init__(self, *args):
5556
self._step = step
5657
self._len = (stop - start) // step + bool((stop - start) % step)
5758

59+
@property
60+
def start(self):
61+
return self._start
62+
63+
@property
64+
def stop(self):
65+
return self._stop
66+
67+
@property
68+
def step(self):
69+
return self._step
70+
5871
def __repr__(self):
5972
if self._step == 1:
6073
return 'range(%d, %d)' % (self._start, self._stop)
@@ -94,12 +107,7 @@ def __contains__(self, value):
94107
return False
95108

96109
def __reversed__(self):
97-
"""Return a range which represents a sequence whose
98-
contents are the same as the sequence this range
99-
represents, but in the opposite order."""
100-
sign = self._step / abs(self._step)
101-
last = self._start + ((self._len - 1) * self._step)
102-
return newrange(last, self._start - sign, -1 * self._step)
110+
return iter(self[::-1])
103111

104112
def __getitem__(self, index):
105113
"""Return the element at position ``index`` in the sequence
@@ -119,40 +127,34 @@ def __getitem_slice(self, slce):
119127
of the sequence represented by this range.
120128
"""
121129
start, stop, step = slce.indices(self._len)
122-
return newrange(self[start], stop + self._start, step * self._step)
130+
return newrange(self._start + self._step*start,
131+
self._start + stop,
132+
self._step * step)
123133

124134
def __iter__(self):
125135
"""Return an iterator which enumerates the elements of the
126136
sequence this range represents."""
127-
return rangeiterator(self)
137+
return range_iterator(self)
128138

129139

130-
class rangeiterator(Iterator):
140+
class range_iterator(Iterator):
131141
"""An iterator for a :class:`range`.
132142
"""
133-
134-
def __init__(self, rangeobj):
135-
self._range = rangeobj
136-
137-
# Intialize the "last outputted value" to the value
138-
# just before the first value; this simplifies next()
139-
self._last = self._range._start - self._range._step
140-
self._count = 0
143+
def __init__(self, range_):
144+
self._stepper = islice(_count(range_.start, range_.step), len(range_))
141145

142146
def __iter__(self):
143-
"""An iterator is already an iterator, so return ``self``.
144-
"""
145147
return self
146148

147149
def next(self):
148-
"""Return the next element in the sequence represented
149-
by the range we are iterating, or raise StopIteration
150-
if we have passed the end of the sequence."""
151-
self._last += self._range._step
152-
self._count += 1
153-
if self._count > self._range._len:
154-
raise StopIteration()
155-
return self._last
150+
return next(self._stepper)
151+
152+
153+
# itertools.count in Py 2.6 doesn't accept a step parameter
154+
def _count(start=0, step=1):
155+
while True:
156+
yield start
157+
start += step
156158

157159

158160
__all__ = ['newrange']

tests/test_future/test_range.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,29 @@
66
from future.builtins import range
77
from future.tests.base import unittest
88

9-
from collections import Sequence
9+
from collections import Iterator, Sequence
1010

1111

1212
class RangeTests(unittest.TestCase):
1313
def test_range(self):
1414
self.assertTrue(isinstance(range(0), Sequence))
15+
self.assertTrue(isinstance(reversed(range(0)), Iterator))
1516

1617
def test_bool_range(self):
1718
self.assertFalse(range(0))
1819
self.assertTrue(range(1))
1920
self.assertFalse(range(1, 1))
2021
self.assertFalse(range(5, 2))
2122

22-
def test_equality(self):
23+
def test_equality_range(self):
2324
self.assertEqual(range(7), range(7))
2425
self.assertEqual(range(0), range(1, 1))
2526
self.assertEqual(range(0, 10, 3), range(0, 11, 3))
2627

28+
def test_slice_empty_range(self):
29+
self.assertEqual(range(0)[:], range(0))
30+
self.assertEqual(range(0)[::-1], range(-1, -1, -1))
31+
2732
def test_slice_range(self):
2833
r = range(8)
2934
self.assertEqual(r[:], range(8))
@@ -99,6 +104,16 @@ def test_slice_zero_step(self):
99104
with self.assertRaisesRegexp(ValueError, msg):
100105
range(8)[::0]
101106

107+
def test_properties(self):
108+
# Exception string differs between PY2/3
109+
r = range(0)
110+
with self.assertRaises(AttributeError):
111+
r.start = 0
112+
with self.assertRaises(AttributeError):
113+
r.stop = 0
114+
with self.assertRaises(AttributeError):
115+
r.step = 0
116+
102117

103118
if __name__ == '__main__':
104119
unittest.main()

0 commit comments

Comments
 (0)