diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 50f03aca97447..7a34e64724245 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -9,7 +9,7 @@ from pandas._libs.lib import is_datetime_array from pandas._libs.tslibs import parsing -from pandas.compat import range, u +from pandas.compat import range, u, set_function_name from pandas.compat.numpy import function as nv from pandas import compat @@ -3893,7 +3893,8 @@ def _evaluate_compare(self, other): except TypeError: return result - return _evaluate_compare + name = '__{name}__'.format(name=op.__name__) + return set_function_name(_evaluate_compare, name, cls) cls.__eq__ = _make_compare(operator.eq) cls.__ne__ = _make_compare(operator.ne) diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 84d4585f0a56c..d09e5447431ce 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -722,7 +722,7 @@ def _codes_for_groupby(self, sort): def _add_comparison_methods(cls): """ add in comparison methods """ - def _make_compare(op): + def _make_compare(opname): def _evaluate_compare(self, other): # if we have a Categorical type, then must have the same @@ -745,9 +745,9 @@ def _evaluate_compare(self, other): "have the same categories and ordered " "attributes") - return getattr(self.values, op)(other) + return getattr(self.values, opname)(other) - return _evaluate_compare + return compat.set_function_name(_evaluate_compare, opname, cls) cls.__eq__ = _make_compare('__eq__') cls.__ne__ = _make_compare('__ne__') diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 3a11c80ecba64..a2ed2ff9bce5e 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -98,7 +98,7 @@ def f(self): return property(f) -def _dt_index_cmp(opname, nat_result=False): +def _dt_index_cmp(opname, cls, nat_result=False): """ Wrap comparison operations to convert datetime-like to datetime64 """ @@ -135,7 +135,7 @@ def wrapper(self, other): return result return Index(result) - return wrapper + return compat.set_function_name(wrapper, opname, cls) def _ensure_datetime64(other): @@ -277,12 +277,15 @@ def _join_i8_wrapper(joinf, **kwargs): libjoin.left_join_indexer_unique_int64, with_indexers=False) _arrmap = None - __eq__ = _dt_index_cmp('__eq__') - __ne__ = _dt_index_cmp('__ne__', nat_result=True) - __lt__ = _dt_index_cmp('__lt__') - __gt__ = _dt_index_cmp('__gt__') - __le__ = _dt_index_cmp('__le__') - __ge__ = _dt_index_cmp('__ge__') + @classmethod + def _add_comparison_methods(cls): + """ add in comparison methods """ + cls.__eq__ = _dt_index_cmp('__eq__', cls) + cls.__ne__ = _dt_index_cmp('__ne__', cls, nat_result=True) + cls.__lt__ = _dt_index_cmp('__lt__', cls) + cls.__gt__ = _dt_index_cmp('__gt__', cls) + cls.__le__ = _dt_index_cmp('__le__', cls) + cls.__ge__ = _dt_index_cmp('__ge__', cls) _engine_type = libindex.DatetimeEngine @@ -1599,14 +1602,15 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): else: raise - # alias to offset - def _get_freq(self): + @property + def freq(self): + """get/set the frequency of the Index""" return self.offset - def _set_freq(self, value): + @freq.setter + def freq(self, value): + """get/set the frequency of the Index""" self.offset = value - freq = property(fget=_get_freq, fset=_set_freq, - doc="get/set the frequency of the Index") year = _field_accessor('year', 'Y', "The year of the datetime") month = _field_accessor('month', 'M', @@ -2014,6 +2018,7 @@ def to_julian_date(self): ) / 24.0) +DatetimeIndex._add_comparison_methods() DatetimeIndex._add_numeric_methods_disabled() DatetimeIndex._add_logical_methods_disabled() DatetimeIndex._add_datetimelike_methods() diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 76004994ae38a..6535eee386e8b 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -77,7 +77,7 @@ def dt64arr_to_periodarr(data, freq, tz): _DIFFERENT_FREQ_INDEX = period._DIFFERENT_FREQ_INDEX -def _period_index_cmp(opname, nat_result=False): +def _period_index_cmp(opname, cls, nat_result=False): """ Wrap comparison operations to convert datetime-like to datetime64 """ @@ -115,7 +115,8 @@ def wrapper(self, other): result[self._isnan] = nat_result return result - return wrapper + + return compat.set_function_name(wrapper, opname, cls) def _new_PeriodIndex(cls, **d): @@ -227,12 +228,15 @@ class PeriodIndex(DatelikeOps, DatetimeIndexOpsMixin, Int64Index): _engine_type = libindex.PeriodEngine - __eq__ = _period_index_cmp('__eq__') - __ne__ = _period_index_cmp('__ne__', nat_result=True) - __lt__ = _period_index_cmp('__lt__') - __gt__ = _period_index_cmp('__gt__') - __le__ = _period_index_cmp('__le__') - __ge__ = _period_index_cmp('__ge__') + @classmethod + def _add_comparison_methods(cls): + """ add in comparison methods """ + cls.__eq__ = _period_index_cmp('__eq__', cls) + cls.__ne__ = _period_index_cmp('__ne__', cls, nat_result=True) + cls.__lt__ = _period_index_cmp('__lt__', cls) + cls.__gt__ = _period_index_cmp('__gt__', cls) + cls.__le__ = _period_index_cmp('__le__', cls) + cls.__ge__ = _period_index_cmp('__ge__', cls) def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None, periods=None, copy=False, name=None, tz=None, dtype=None, @@ -1106,6 +1110,7 @@ def tz_localize(self, tz, infer_dst=False): raise NotImplementedError("Not yet implemented for PeriodIndex") +PeriodIndex._add_comparison_methods() PeriodIndex._add_numeric_methods_disabled() PeriodIndex._add_logical_methods_disabled() PeriodIndex._add_datetimelike_methods() diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index eb4a9ce7e1439..22fb7c255b12c 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -52,7 +52,7 @@ def f(self): return property(f) -def _td_index_cmp(opname, nat_result=False): +def _td_index_cmp(opname, cls, nat_result=False): """ Wrap comparison operations to convert timedelta-like to timedelta64 """ @@ -93,7 +93,7 @@ def wrapper(self, other): return result return Index(result) - return wrapper + return compat.set_function_name(wrapper, opname, cls) class TimedeltaIndex(DatetimeIndexOpsMixin, TimelikeOps, Int64Index): @@ -180,12 +180,15 @@ def _join_i8_wrapper(joinf, **kwargs): _datetimelike_methods = ["to_pytimedelta", "total_seconds", "round", "floor", "ceil"] - __eq__ = _td_index_cmp('__eq__') - __ne__ = _td_index_cmp('__ne__', nat_result=True) - __lt__ = _td_index_cmp('__lt__') - __gt__ = _td_index_cmp('__gt__') - __le__ = _td_index_cmp('__le__') - __ge__ = _td_index_cmp('__ge__') + @classmethod + def _add_comparison_methods(cls): + """ add in comparison methods """ + cls.__eq__ = _td_index_cmp('__eq__', cls) + cls.__ne__ = _td_index_cmp('__ne__', cls, nat_result=True) + cls.__lt__ = _td_index_cmp('__lt__', cls) + cls.__gt__ = _td_index_cmp('__gt__', cls) + cls.__le__ = _td_index_cmp('__le__', cls) + cls.__ge__ = _td_index_cmp('__ge__', cls) _engine_type = libindex.TimedeltaEngine @@ -912,6 +915,7 @@ def delete(self, loc): return TimedeltaIndex(new_tds, name=self.name, freq=freq) +TimedeltaIndex._add_comparison_methods() TimedeltaIndex._add_numeric_methods() TimedeltaIndex._add_logical_methods_disabled() TimedeltaIndex._add_datetimelike_methods() diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 307cda7f2d1cb..c55f53601848c 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -2174,3 +2174,11 @@ class TestIndexUtils(object): def test_ensure_index_from_sequences(self, data, names, expected): result = _ensure_index_from_sequences(data, names) tm.assert_index_equal(result, expected) + + +@pytest.mark.parametrize('opname', ['eq', 'ne', 'le', 'lt', 'ge', 'gt']) +def test_generated_op_names(opname, indices): + index = indices + opname = '__{name}__'.format(name=opname) + method = getattr(index, opname) + assert method.__name__ == opname