Skip to content

Commit 873e22e

Browse files
jschendeljreback
authored andcommitted
ENH: Add public start, stop, and step attributes to RangeIndex (#25720)
1 parent 79205ea commit 873e22e

File tree

3 files changed

+63
-42
lines changed

3 files changed

+63
-42
lines changed

doc/source/whatsnew/v0.25.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Other Enhancements
2626
- :meth:`DataFrame.set_index` now works for instances of ``abc.Iterator``, provided their output is of the same length as the calling frame (:issue:`22484`, :issue:`24984`)
2727
- :meth:`DatetimeIndex.union` now supports the ``sort`` argument. The behaviour of the sort parameter matches that of :meth:`Index.union` (:issue:`24994`)
2828
- :meth:`DataFrame.rename` now supports the ``errors`` argument to raise errors when attempting to rename nonexistent keys (:issue:`13473`)
29+
- :class:`RangeIndex` has gained :attr:`~RangeIndex.start`, :attr:`~RangeIndex.stop`, and :attr:`~RangeIndex.step` attributes (:issue:`25710`)
2930

3031
.. _whatsnew_0250.api_breaking:
3132

pandas/core/indexes/range.py

+26-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ class RangeIndex(Int64Index):
4848
4949
Attributes
5050
----------
51-
None
51+
start
52+
stop
53+
step
5254
5355
Methods
5456
-------
@@ -209,6 +211,29 @@ def _format_data(self, name=None):
209211
return None
210212

211213
# --------------------------------------------------------------------
214+
@property
215+
def start(self):
216+
"""
217+
The value of the `start` parameter (or ``0`` if this was not supplied)
218+
"""
219+
# GH 25710
220+
return self._start
221+
222+
@property
223+
def stop(self):
224+
"""
225+
The value of the `stop` parameter
226+
"""
227+
# GH 25710
228+
return self._stop
229+
230+
@property
231+
def step(self):
232+
"""
233+
The value of the `step` parameter (or ``1`` if this was not supplied)
234+
"""
235+
# GH 25710
236+
return self._step
212237

213238
@cache_readonly
214239
def nbytes(self):

pandas/tests/indexes/test_range.py

+36-41
Original file line numberDiff line numberDiff line change
@@ -35,47 +35,25 @@ def test_too_many_names(self):
3535
with pytest.raises(ValueError, match="^Length"):
3636
self.index.names = ["roger", "harold"]
3737

38-
def test_constructor(self):
39-
index = RangeIndex(5)
40-
expected = np.arange(5, dtype=np.int64)
41-
assert isinstance(index, RangeIndex)
42-
assert index._start == 0
43-
assert index._stop == 5
44-
assert index._step == 1
45-
assert index.name is None
46-
tm.assert_index_equal(Index(expected), index)
47-
48-
index = RangeIndex(1, 5)
49-
expected = np.arange(1, 5, dtype=np.int64)
50-
assert isinstance(index, RangeIndex)
51-
assert index._start == 1
52-
tm.assert_index_equal(Index(expected), index)
53-
54-
index = RangeIndex(1, 5, 2)
55-
expected = np.arange(1, 5, 2, dtype=np.int64)
56-
assert isinstance(index, RangeIndex)
57-
assert index._step == 2
58-
tm.assert_index_equal(Index(expected), index)
59-
60-
for index in [RangeIndex(0), RangeIndex(start=0), RangeIndex(stop=0),
61-
RangeIndex(0, 0)]:
62-
expected = np.empty(0, dtype=np.int64)
63-
assert isinstance(index, RangeIndex)
64-
assert index._start == 0
65-
assert index._stop == 0
66-
assert index._step == 1
67-
tm.assert_index_equal(Index(expected), index)
68-
69-
for index in [RangeIndex(0, name='Foo'),
70-
RangeIndex(start=0, name='Foo'),
71-
RangeIndex(stop=0, name='Foo'),
72-
RangeIndex(0, 0, name='Foo')]:
73-
assert isinstance(index, RangeIndex)
74-
assert index.name == 'Foo'
75-
76-
# we don't allow on a bare Index
77-
with pytest.raises(TypeError):
78-
Index(0, 1000)
38+
@pytest.mark.parametrize('name', [None, 'foo'])
39+
@pytest.mark.parametrize('args, kwargs, start, stop, step', [
40+
((5,), dict(), 0, 5, 1),
41+
((1, 5), dict(), 1, 5, 1),
42+
((1, 5, 2), dict(), 1, 5, 2),
43+
((0,), dict(), 0, 0, 1),
44+
((0, 0), dict(), 0, 0, 1),
45+
(tuple(), dict(start=0), 0, 0, 1),
46+
(tuple(), dict(stop=0), 0, 0, 1)])
47+
def test_constructor(self, args, kwargs, start, stop, step, name):
48+
result = RangeIndex(*args, name=name, **kwargs)
49+
expected = Index(np.arange(start, stop, step, dtype=np.int64),
50+
name=name)
51+
assert isinstance(result, RangeIndex)
52+
assert result._start == start
53+
assert result._stop == stop
54+
assert result._step == step
55+
assert result.name is name
56+
tm.assert_index_equal(result, expected)
7957

8058
def test_constructor_invalid_args(self):
8159
msg = "RangeIndex\\(\\.\\.\\.\\) must be called with integers"
@@ -92,6 +70,12 @@ def test_constructor_invalid_args(self):
9270
with pytest.raises(TypeError):
9371
RangeIndex(i)
9472

73+
# we don't allow on a bare Index
74+
msg = (r'Index\(\.\.\.\) must be called with a collection of some '
75+
r'kind, 0 was passed')
76+
with pytest.raises(TypeError, match=msg):
77+
Index(0, 1000)
78+
9579
def test_constructor_same(self):
9680

9781
# pass thru w and w/o copy
@@ -172,6 +156,17 @@ def test_constructor_corner(self):
172156
with pytest.raises(TypeError):
173157
RangeIndex(1, 5, dtype='float64')
174158

159+
@pytest.mark.parametrize('index, start, stop, step', [
160+
(RangeIndex(5), 0, 5, 1),
161+
(RangeIndex(0, 5), 0, 5, 1),
162+
(RangeIndex(5, step=2), 0, 5, 2),
163+
(RangeIndex(1, 5, 2), 1, 5, 2)])
164+
def test_start_stop_step_attrs(self, index, start, stop, step):
165+
# GH 25710
166+
assert index.start == start
167+
assert index.stop == stop
168+
assert index.step == step
169+
175170
def test_copy(self):
176171
i = RangeIndex(5, name='Foo')
177172
i_copy = i.copy()

0 commit comments

Comments
 (0)