Skip to content

Commit 0ac130d

Browse files
jbrockmendelaeltanawy
authored andcommitted
Implement delegate_names to allow decorating delegated attributes (pandas-dev#22599)
1 parent 1bfe0c4 commit 0ac130d

File tree

4 files changed

+72
-57
lines changed

4 files changed

+72
-57
lines changed

pandas/core/accessor.py

+32
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,38 @@ def f(self, *args, **kwargs):
105105
setattr(cls, name, f)
106106

107107

108+
def delegate_names(delegate, accessors, typ, overwrite=False):
109+
"""
110+
Add delegated names to a class using a class decorator. This provides
111+
an alternative usage to directly calling `_add_delegate_accessors`
112+
below a class definition.
113+
114+
Parameters
115+
----------
116+
delegate : the class to get methods/properties & doc-strings
117+
acccessors : string list of accessors to add
118+
typ : 'property' or 'method'
119+
overwrite : boolean, default False
120+
overwrite the method/property in the target class if it exists
121+
122+
Returns
123+
-------
124+
decorator
125+
126+
Examples
127+
--------
128+
@delegate_names(Categorical, ["categories", "ordered"], "property")
129+
class CategoricalAccessor(PandasDelegate):
130+
[...]
131+
"""
132+
def add_delegate_accessors(cls):
133+
cls._add_delegate_accessors(delegate, accessors, typ,
134+
overwrite=overwrite)
135+
return cls
136+
137+
return add_delegate_accessors
138+
139+
108140
# Ported with modifications from xarray
109141
# https://github.com/pydata/xarray/blob/master/xarray/core/extensions.py
110142
# 1. We don't need to catch and re-raise AttributeErrors as RuntimeErrors

pandas/core/arrays/categorical.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
is_dict_like)
3535

3636
from pandas.core.algorithms import factorize, take_1d, unique1d, take
37-
from pandas.core.accessor import PandasDelegate
37+
from pandas.core.accessor import PandasDelegate, delegate_names
3838
from pandas.core.base import (PandasObject,
3939
NoNewAttributesMixin, _shared_docs)
4040
import pandas.core.common as com
@@ -2365,6 +2365,15 @@ def isin(self, values):
23652365
# The Series.cat accessor
23662366

23672367

2368+
@delegate_names(delegate=Categorical,
2369+
accessors=["categories", "ordered"],
2370+
typ="property")
2371+
@delegate_names(delegate=Categorical,
2372+
accessors=["rename_categories", "reorder_categories",
2373+
"add_categories", "remove_categories",
2374+
"remove_unused_categories", "set_categories",
2375+
"as_ordered", "as_unordered"],
2376+
typ="method")
23682377
class CategoricalAccessor(PandasDelegate, PandasObject, NoNewAttributesMixin):
23692378
"""
23702379
Accessor object for categorical properties of the Series values.
@@ -2424,15 +2433,6 @@ def _delegate_method(self, name, *args, **kwargs):
24242433
return Series(res, index=self.index, name=self.name)
24252434

24262435

2427-
CategoricalAccessor._add_delegate_accessors(delegate=Categorical,
2428-
accessors=["categories",
2429-
"ordered"],
2430-
typ='property')
2431-
CategoricalAccessor._add_delegate_accessors(delegate=Categorical, accessors=[
2432-
"rename_categories", "reorder_categories", "add_categories",
2433-
"remove_categories", "remove_unused_categories", "set_categories",
2434-
"as_ordered", "as_unordered"], typ='method')
2435-
24362436
# utility routines
24372437

24382438

pandas/core/indexes/accessors.py

+19-31
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
is_timedelta64_dtype, is_categorical_dtype,
1313
is_list_like)
1414

15-
from pandas.core.accessor import PandasDelegate
15+
from pandas.core.accessor import PandasDelegate, delegate_names
1616
from pandas.core.base import NoNewAttributesMixin, PandasObject
1717
from pandas.core.indexes.datetimes import DatetimeIndex
1818
from pandas.core.indexes.period import PeriodIndex
@@ -110,6 +110,12 @@ def _delegate_method(self, name, *args, **kwargs):
110110
return result
111111

112112

113+
@delegate_names(delegate=DatetimeIndex,
114+
accessors=DatetimeIndex._datetimelike_ops,
115+
typ="property")
116+
@delegate_names(delegate=DatetimeIndex,
117+
accessors=DatetimeIndex._datetimelike_methods,
118+
typ="method")
113119
class DatetimeProperties(Properties):
114120
"""
115121
Accessor object for datetimelike properties of the Series values.
@@ -175,16 +181,12 @@ def freq(self):
175181
return self._get_values().inferred_freq
176182

177183

178-
DatetimeProperties._add_delegate_accessors(
179-
delegate=DatetimeIndex,
180-
accessors=DatetimeIndex._datetimelike_ops,
181-
typ='property')
182-
DatetimeProperties._add_delegate_accessors(
183-
delegate=DatetimeIndex,
184-
accessors=DatetimeIndex._datetimelike_methods,
185-
typ='method')
186-
187-
184+
@delegate_names(delegate=TimedeltaIndex,
185+
accessors=TimedeltaIndex._datetimelike_ops,
186+
typ="property")
187+
@delegate_names(delegate=TimedeltaIndex,
188+
accessors=TimedeltaIndex._datetimelike_methods,
189+
typ="method")
188190
class TimedeltaProperties(Properties):
189191
"""
190192
Accessor object for datetimelike properties of the Series values.
@@ -268,16 +270,12 @@ def freq(self):
268270
return self._get_values().inferred_freq
269271

270272

271-
TimedeltaProperties._add_delegate_accessors(
272-
delegate=TimedeltaIndex,
273-
accessors=TimedeltaIndex._datetimelike_ops,
274-
typ='property')
275-
TimedeltaProperties._add_delegate_accessors(
276-
delegate=TimedeltaIndex,
277-
accessors=TimedeltaIndex._datetimelike_methods,
278-
typ='method')
279-
280-
273+
@delegate_names(delegate=PeriodIndex,
274+
accessors=PeriodIndex._datetimelike_ops,
275+
typ="property")
276+
@delegate_names(delegate=PeriodIndex,
277+
accessors=PeriodIndex._datetimelike_methods,
278+
typ="method")
281279
class PeriodProperties(Properties):
282280
"""
283281
Accessor object for datetimelike properties of the Series values.
@@ -293,16 +291,6 @@ class PeriodProperties(Properties):
293291
"""
294292

295293

296-
PeriodProperties._add_delegate_accessors(
297-
delegate=PeriodIndex,
298-
accessors=PeriodIndex._datetimelike_ops,
299-
typ='property')
300-
PeriodProperties._add_delegate_accessors(
301-
delegate=PeriodIndex,
302-
accessors=PeriodIndex._datetimelike_methods,
303-
typ='method')
304-
305-
306294
class CombinedDatetimelikeProperties(DatetimeProperties, TimedeltaProperties):
307295

308296
def __new__(cls, data):

pandas/core/indexes/category.py

+11-16
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@
3030
_index_doc_kwargs.update(dict(target_klass='CategoricalIndex'))
3131

3232

33+
@accessor.delegate_names(
34+
delegate=Categorical,
35+
accessors=["rename_categories",
36+
"reorder_categories",
37+
"add_categories",
38+
"remove_categories",
39+
"remove_unused_categories",
40+
"set_categories",
41+
"as_ordered", "as_unordered",
42+
"min", "max"],
43+
typ='method', overwrite=True)
3344
class CategoricalIndex(Index, accessor.PandasDelegate):
3445
"""
3546
@@ -835,24 +846,8 @@ def _delegate_method(self, name, *args, **kwargs):
835846
return res
836847
return CategoricalIndex(res, name=self.name)
837848

838-
@classmethod
839-
def _add_accessors(cls):
840-
""" add in Categorical accessor methods """
841-
842-
CategoricalIndex._add_delegate_accessors(
843-
delegate=Categorical, accessors=["rename_categories",
844-
"reorder_categories",
845-
"add_categories",
846-
"remove_categories",
847-
"remove_unused_categories",
848-
"set_categories",
849-
"as_ordered", "as_unordered",
850-
"min", "max"],
851-
typ='method', overwrite=True)
852-
853849

854850
CategoricalIndex._add_numeric_methods_add_sub_disabled()
855851
CategoricalIndex._add_numeric_methods_disabled()
856852
CategoricalIndex._add_logical_methods_disabled()
857853
CategoricalIndex._add_comparison_methods()
858-
CategoricalIndex._add_accessors()

0 commit comments

Comments
 (0)