Skip to content

Commit 3a8def4

Browse files
jbrockmendelKevin D Smith
authored and
Kevin D Smith
committed
TYP: define RangeIndex methods non-dynamically (pandas-dev#36931)
1 parent c049748 commit 3a8def4

File tree

2 files changed

+80
-72
lines changed

2 files changed

+80
-72
lines changed

pandas/core/indexes/range.py

+80-69
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from pandas._libs import index as libindex
1010
from pandas._libs.lib import no_default
1111
from pandas._typing import Label
12-
import pandas.compat as compat
1312
from pandas.compat.numpy import function as nv
1413
from pandas.util._decorators import Appender, cache_readonly, doc
1514

@@ -812,83 +811,95 @@ def any(self, *args, **kwargs) -> bool:
812811

813812
# --------------------------------------------------------------------
814813

815-
@classmethod
816-
def _add_numeric_methods_binary(cls):
817-
""" add in numeric methods, specialized to RangeIndex """
818-
819-
def _make_evaluate_binop(op, step=False):
820-
"""
821-
Parameters
822-
----------
823-
op : callable that accepts 2 params
824-
perform the binary op
825-
step : callable, optional, default to False
826-
op to apply to the step parm if not None
827-
if False, use the existing step
828-
"""
829-
830-
@unpack_zerodim_and_defer(op.__name__)
831-
def _evaluate_numeric_binop(self, other):
832-
if isinstance(other, ABCTimedeltaIndex):
833-
# Defer to TimedeltaIndex implementation
834-
return NotImplemented
835-
elif isinstance(other, (timedelta, np.timedelta64)):
836-
# GH#19333 is_integer evaluated True on timedelta64,
837-
# so we need to catch these explicitly
838-
return op(self._int64index, other)
839-
elif is_timedelta64_dtype(other):
840-
# Must be an np.ndarray; GH#22390
841-
return op(self._int64index, other)
842-
843-
other = extract_array(other, extract_numpy=True)
844-
attrs = self._get_attributes_dict()
845-
846-
left, right = self, other
814+
def _arith_method(self, other, op, step=False):
815+
"""
816+
Parameters
817+
----------
818+
other : Any
819+
op : callable that accepts 2 params
820+
perform the binary op
821+
step : callable, optional, default to False
822+
op to apply to the step parm if not None
823+
if False, use the existing step
824+
"""
825+
826+
if isinstance(other, ABCTimedeltaIndex):
827+
# Defer to TimedeltaIndex implementation
828+
return NotImplemented
829+
elif isinstance(other, (timedelta, np.timedelta64)):
830+
# GH#19333 is_integer evaluated True on timedelta64,
831+
# so we need to catch these explicitly
832+
return op(self._int64index, other)
833+
elif is_timedelta64_dtype(other):
834+
# Must be an np.ndarray; GH#22390
835+
return op(self._int64index, other)
836+
837+
other = extract_array(other, extract_numpy=True)
838+
attrs = self._get_attributes_dict()
839+
840+
left, right = self, other
847841

848-
try:
849-
# apply if we have an override
850-
if step:
851-
with np.errstate(all="ignore"):
852-
rstep = step(left.step, right)
842+
try:
843+
# apply if we have an override
844+
if step:
845+
with np.errstate(all="ignore"):
846+
rstep = step(left.step, right)
847+
848+
# we don't have a representable op
849+
# so return a base index
850+
if not is_integer(rstep) or not rstep:
851+
raise ValueError
852+
853+
else:
854+
rstep = left.step
855+
856+
with np.errstate(all="ignore"):
857+
rstart = op(left.start, right)
858+
rstop = op(left.stop, right)
859+
860+
result = type(self)(rstart, rstop, rstep, **attrs)
853861

854-
# we don't have a representable op
855-
# so return a base index
856-
if not is_integer(rstep) or not rstep:
857-
raise ValueError
862+
# for compat with numpy / Int64Index
863+
# even if we can represent as a RangeIndex, return
864+
# as a Float64Index if we have float-like descriptors
865+
if not all(is_integer(x) for x in [rstart, rstop, rstep]):
866+
result = result.astype("float64")
858867

859-
else:
860-
rstep = left.step
868+
return result
861869

862-
with np.errstate(all="ignore"):
863-
rstart = op(left.start, right)
864-
rstop = op(left.stop, right)
870+
except (ValueError, TypeError, ZeroDivisionError):
871+
# Defer to Int64Index implementation
872+
return op(self._int64index, other)
873+
# TODO: Do attrs get handled reliably?
865874

866-
result = type(self)(rstart, rstop, rstep, **attrs)
875+
@unpack_zerodim_and_defer("__add__")
876+
def __add__(self, other):
877+
return self._arith_method(other, operator.add)
867878

868-
# for compat with numpy / Int64Index
869-
# even if we can represent as a RangeIndex, return
870-
# as a Float64Index if we have float-like descriptors
871-
if not all(is_integer(x) for x in [rstart, rstop, rstep]):
872-
result = result.astype("float64")
879+
@unpack_zerodim_and_defer("__radd__")
880+
def __radd__(self, other):
881+
return self._arith_method(other, ops.radd)
873882

874-
return result
883+
@unpack_zerodim_and_defer("__sub__")
884+
def __sub__(self, other):
885+
return self._arith_method(other, operator.sub)
875886

876-
except (ValueError, TypeError, ZeroDivisionError):
877-
# Defer to Int64Index implementation
878-
return op(self._int64index, other)
879-
# TODO: Do attrs get handled reliably?
887+
@unpack_zerodim_and_defer("__rsub__")
888+
def __rsub__(self, other):
889+
return self._arith_method(other, ops.rsub)
880890

881-
name = f"__{op.__name__}__"
882-
return compat.set_function_name(_evaluate_numeric_binop, name, cls)
891+
@unpack_zerodim_and_defer("__mul__")
892+
def __mul__(self, other):
893+
return self._arith_method(other, operator.mul, step=operator.mul)
883894

884-
cls.__add__ = _make_evaluate_binop(operator.add)
885-
cls.__radd__ = _make_evaluate_binop(ops.radd)
886-
cls.__sub__ = _make_evaluate_binop(operator.sub)
887-
cls.__rsub__ = _make_evaluate_binop(ops.rsub)
888-
cls.__mul__ = _make_evaluate_binop(operator.mul, step=operator.mul)
889-
cls.__rmul__ = _make_evaluate_binop(ops.rmul, step=ops.rmul)
890-
cls.__truediv__ = _make_evaluate_binop(operator.truediv, step=operator.truediv)
891-
cls.__rtruediv__ = _make_evaluate_binop(ops.rtruediv, step=ops.rtruediv)
895+
@unpack_zerodim_and_defer("__rmul__")
896+
def __rmul__(self, other):
897+
return self._arith_method(other, ops.rmul, step=ops.rmul)
892898

899+
@unpack_zerodim_and_defer("__truediv__")
900+
def __truediv__(self, other):
901+
return self._arith_method(other, operator.truediv, step=operator.truediv)
893902

894-
RangeIndex._add_numeric_methods()
903+
@unpack_zerodim_and_defer("__rtruediv__")
904+
def __rtruediv__(self, other):
905+
return self._arith_method(other, ops.rtruediv, step=ops.rtruediv)

setup.cfg

-3
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,6 @@ check_untyped_defs=False
193193
[mypy-pandas.core.indexes.multi]
194194
check_untyped_defs=False
195195

196-
[mypy-pandas.core.indexes.range]
197-
check_untyped_defs=False
198-
199196
[mypy-pandas.core.internals.blocks]
200197
check_untyped_defs=False
201198

0 commit comments

Comments
 (0)