Skip to content

implement Delegator class #17662

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 91 additions & 23 deletions pandas/core/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,38 +93,106 @@ def _add_delegate_accessors(cls, delegate, accessors, typ,
overwrite : boolean, default False
overwrite the method/property in the target class if it exists
"""
for name in accessors:

def _create_delegator_property(name):
if typ == 'property':
f = Delegator.create_delegator_property(name, delegate)
else:
f = Delegator.create_delegator_method(name, delegate)

def _getter(self):
return self._delegate_property_get(name)
# don't overwrite existing methods/properties
if overwrite or not hasattr(cls, name):
setattr(cls, name, f)

def _setter(self, new_values):
return self._delegate_property_set(name, new_values)

_getter.__name__ = name
_setter.__name__ = name
class Delegator(object):
""" Delegator class contains methods that are used by PandasDelegate
and Accessor subclasses, but that so not ultimately belong in
the namespaces of user-facing classes.

return property(fget=_getter, fset=_setter,
doc=getattr(delegate, name).__doc__)
Many of these methods *could* be module-level functions, but are
retained as staticmethods for organization purposes.
"""

def _create_delegator_method(name):
@staticmethod
def create_delegator_property(name, delegate):
# Note: we really only need the `delegate` here for the docstring

def f(self, *args, **kwargs):
return self._delegate_method(name, *args, **kwargs)
def _getter(self):
return self._delegate_property_get(name)

f.__name__ = name
f.__doc__ = getattr(delegate, name).__doc__
def _setter(self, new_values):
return self._delegate_property_set(name, new_values)
# TODO: not hit in tests; not sure this is something we
# really want anyway

return f
_getter.__name__ = name
_setter.__name__ = name
_doc = getattr(delegate, name).__doc__
return property(fget=_getter, fset=_setter, doc=_doc)

for name in accessors:
@staticmethod
def create_delegator_method(name, delegate):
# Note: we really only need the `delegate` here for the docstring

if typ == 'property':
f = _create_delegator_property(name)
else:
f = _create_delegator_method(name)
def func(self, *args, **kwargs):
return self._delegate_method(name, *args, **kwargs)

# don't overwrite existing methods/properties
if overwrite or not hasattr(cls, name):
setattr(cls, name, f)
func.__name__ = name
func.__doc__ = getattr(delegate, name).__doc__
return func

@staticmethod
def delegate_names(delegate, accessors, typ, overwrite=False):
"""
delegate_names decorates class definitions, e.g:

@delegate_names(Categorical, ["categories", "ordered"], "property")
class CategoricalAccessor(PandasDelegate):

@classmethod
def _make_accessor(cls, data):
[...]


The motivation is that we would like to keep as much of a class's
internals inside the class definition. For things that we cannot
keep directly in the class definition, a decorator is more directly
tied to the definition than a method call outside the definition.

"""
# Note: we really only need the `delegate` here for the docstring

def add_delegate_accessors(cls):
"""
add accessors to cls from the delegate class

Parameters
----------
cls : the class to add the methods/properties to
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
"""
for name in accessors:
if typ == "property":
func = Delegator.create_delegator_property(name, delegate)
else:
func = Delegator.create_delegator_method(name, delegate)

# don't overwrite existing methods/properties unless
# specifically told to do so
if overwrite or not hasattr(cls, name):
setattr(cls, name, func)

return cls

return add_delegate_accessors


wrap_delegate_names = Delegator.delegate_names
# TODO: the `delegate` arg to `wrap_delegate_names` is really only relevant
# for a docstring. It'd be nice if we didn't require it and could duck-type
# instead.