diff --git a/pandas/core/accessor.py b/pandas/core/accessor.py index 7a853d575aa69..eab529584d1fb 100644 --- a/pandas/core/accessor.py +++ b/pandas/core/accessor.py @@ -105,6 +105,38 @@ def f(self, *args, **kwargs): setattr(cls, name, f) +def delegate_names(delegate, accessors, typ, overwrite=False): + """ + Add delegated names to a class using a class decorator. This provides + an alternative usage to directly calling `_add_delegate_accessors` + below a class definition. + + Parameters + ---------- + delegate : the class to get methods/properties & doc-strings + acccessors : string list of accessors to add + typ : 'property' or 'method' + overwrite : boolean, default False + overwrite the method/property in the target class if it exists + + Returns + ------- + decorator + + Examples + -------- + @delegate_names(Categorical, ["categories", "ordered"], "property") + class CategoricalAccessor(PandasDelegate): + [...] + """ + def add_delegate_accessors(cls): + cls._add_delegate_accessors(delegate, accessors, typ, + overwrite=overwrite) + return cls + + return add_delegate_accessors + + # Ported with modifications from xarray # https://github.com/pydata/xarray/blob/master/xarray/core/extensions.py # 1. We don't need to catch and re-raise AttributeErrors as RuntimeErrors diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 9b7320bf143c2..5410412d5f45b 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -34,7 +34,7 @@ is_dict_like) from pandas.core.algorithms import factorize, take_1d, unique1d, take -from pandas.core.accessor import PandasDelegate +from pandas.core.accessor import PandasDelegate, delegate_names from pandas.core.base import (PandasObject, NoNewAttributesMixin, _shared_docs) import pandas.core.common as com @@ -2365,6 +2365,15 @@ def isin(self, values): # The Series.cat accessor +@delegate_names(delegate=Categorical, + accessors=["categories", "ordered"], + typ="property") +@delegate_names(delegate=Categorical, + accessors=["rename_categories", "reorder_categories", + "add_categories", "remove_categories", + "remove_unused_categories", "set_categories", + "as_ordered", "as_unordered"], + typ="method") class CategoricalAccessor(PandasDelegate, PandasObject, NoNewAttributesMixin): """ Accessor object for categorical properties of the Series values. @@ -2424,15 +2433,6 @@ def _delegate_method(self, name, *args, **kwargs): return Series(res, index=self.index, name=self.name) -CategoricalAccessor._add_delegate_accessors(delegate=Categorical, - accessors=["categories", - "ordered"], - typ='property') -CategoricalAccessor._add_delegate_accessors(delegate=Categorical, accessors=[ - "rename_categories", "reorder_categories", "add_categories", - "remove_categories", "remove_unused_categories", "set_categories", - "as_ordered", "as_unordered"], typ='method') - # utility routines diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 6ab8c4659c31e..a1868980faed3 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -12,7 +12,7 @@ is_timedelta64_dtype, is_categorical_dtype, is_list_like) -from pandas.core.accessor import PandasDelegate +from pandas.core.accessor import PandasDelegate, delegate_names from pandas.core.base import NoNewAttributesMixin, PandasObject from pandas.core.indexes.datetimes import DatetimeIndex from pandas.core.indexes.period import PeriodIndex @@ -110,6 +110,12 @@ def _delegate_method(self, name, *args, **kwargs): return result +@delegate_names(delegate=DatetimeIndex, + accessors=DatetimeIndex._datetimelike_ops, + typ="property") +@delegate_names(delegate=DatetimeIndex, + accessors=DatetimeIndex._datetimelike_methods, + typ="method") class DatetimeProperties(Properties): """ Accessor object for datetimelike properties of the Series values. @@ -175,16 +181,12 @@ def freq(self): return self._get_values().inferred_freq -DatetimeProperties._add_delegate_accessors( - delegate=DatetimeIndex, - accessors=DatetimeIndex._datetimelike_ops, - typ='property') -DatetimeProperties._add_delegate_accessors( - delegate=DatetimeIndex, - accessors=DatetimeIndex._datetimelike_methods, - typ='method') - - +@delegate_names(delegate=TimedeltaIndex, + accessors=TimedeltaIndex._datetimelike_ops, + typ="property") +@delegate_names(delegate=TimedeltaIndex, + accessors=TimedeltaIndex._datetimelike_methods, + typ="method") class TimedeltaProperties(Properties): """ Accessor object for datetimelike properties of the Series values. @@ -268,16 +270,12 @@ def freq(self): return self._get_values().inferred_freq -TimedeltaProperties._add_delegate_accessors( - delegate=TimedeltaIndex, - accessors=TimedeltaIndex._datetimelike_ops, - typ='property') -TimedeltaProperties._add_delegate_accessors( - delegate=TimedeltaIndex, - accessors=TimedeltaIndex._datetimelike_methods, - typ='method') - - +@delegate_names(delegate=PeriodIndex, + accessors=PeriodIndex._datetimelike_ops, + typ="property") +@delegate_names(delegate=PeriodIndex, + accessors=PeriodIndex._datetimelike_methods, + typ="method") class PeriodProperties(Properties): """ Accessor object for datetimelike properties of the Series values. @@ -293,16 +291,6 @@ class PeriodProperties(Properties): """ -PeriodProperties._add_delegate_accessors( - delegate=PeriodIndex, - accessors=PeriodIndex._datetimelike_ops, - typ='property') -PeriodProperties._add_delegate_accessors( - delegate=PeriodIndex, - accessors=PeriodIndex._datetimelike_methods, - typ='method') - - class CombinedDatetimelikeProperties(DatetimeProperties, TimedeltaProperties): def __new__(cls, data): diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index e3a21efe269ce..45703c220a4be 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -30,6 +30,17 @@ _index_doc_kwargs.update(dict(target_klass='CategoricalIndex')) +@accessor.delegate_names( + delegate=Categorical, + accessors=["rename_categories", + "reorder_categories", + "add_categories", + "remove_categories", + "remove_unused_categories", + "set_categories", + "as_ordered", "as_unordered", + "min", "max"], + typ='method', overwrite=True) class CategoricalIndex(Index, accessor.PandasDelegate): """ @@ -835,24 +846,8 @@ def _delegate_method(self, name, *args, **kwargs): return res return CategoricalIndex(res, name=self.name) - @classmethod - def _add_accessors(cls): - """ add in Categorical accessor methods """ - - CategoricalIndex._add_delegate_accessors( - delegate=Categorical, accessors=["rename_categories", - "reorder_categories", - "add_categories", - "remove_categories", - "remove_unused_categories", - "set_categories", - "as_ordered", "as_unordered", - "min", "max"], - typ='method', overwrite=True) - CategoricalIndex._add_numeric_methods_add_sub_disabled() CategoricalIndex._add_numeric_methods_disabled() CategoricalIndex._add_logical_methods_disabled() CategoricalIndex._add_comparison_methods() -CategoricalIndex._add_accessors()