Created
February 10, 2017 16:51
-
-
Save RWJMurphy/8606642bd1af2011fa47898b882b0620 to your computer and use it in GitHub Desktop.
python delegate class decorator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""A class decorator to enable classes to delegate to an attribute.""" | |
def delegate(delegate_class, delegate_name): | |
""" | |
Delegating class decorator. | |
.. code-block:: python | |
class Location(): | |
def __init__(self, x, y): | |
self.x = x | |
self.y = y | |
def move(self, dx, dy): | |
self.x += dx | |
self.y += dy | |
@delegate(Location, 'location') | |
class Mob(): | |
def __init__(self, name, location): | |
self.name = name | |
self.location = location | |
def __str__(self): | |
return "A {name}, at {x},{y}".format( | |
name=self.name, x=self.x, y=self.y) | |
mob = Mob('rat', Location(10, 20)) | |
print(mob) | |
# A rat, at 10,20 | |
mob.move(1, 1) | |
print(mob) | |
# A rat, at 11,21 | |
:param delegate_class: the class of the delegate | |
:type delegate_class: type | |
:param delegate_name: name of the attribute to delegate to | |
:type delegate_name: str | |
""" | |
def delegate_property(delegate_name, attr_name, doc=None): | |
""" | |
A :class:`property` for delegating to an attribute of `self`. | |
:type delegate_name: str | |
:type attr_name: str | |
:type doc: str or None | |
""" | |
return property( | |
lambda self: getattr(getattr(self, delegate_name), attr_name), | |
lambda self, value: setattr( | |
getattr(self, delegate_name), attr_name, value), | |
lambda self: delattr(getattr(self, delegate_name), attr_name), | |
doc=doc) | |
def with_delegating_attributes(parent_class): | |
""" | |
Add delegating attributes to a class. | |
:param parent_class: class to decorate | |
:type parent_class: type | |
""" | |
old_getattr = getattr(parent_class, '__getattr__', None) | |
old_delattr = parent_class.__delattr__ | |
old_dir = parent_class.__dir__ | |
def __getattr__(self, name): | |
if hasattr( | |
getattr(self, | |
delegate_name), name) and not name.startswith('_'): | |
# [maniacal laughter] | |
setattr(self.__class__, name, delegate_property(delegate_name, | |
name)) | |
return getattr(self, name) | |
elif old_getattr: | |
return old_getattr(self, name) | |
else: | |
raise AttributeError( | |
"'{cls}' object has no attribute {name!r}".format( | |
cls=self.__class__.__name__, | |
name=name)) | |
def __delattr__(self, name): | |
if hasattr( | |
getattr(self, | |
delegate_name), name) and not name.startswith('_'): | |
delattr(getattr(self, delegate_name), name) | |
else: | |
old_delattr(self, name) | |
def __dir__(self): | |
return super(object, self).__dir__() + [ | |
attr for attr in old_dir(self) if not attr.startswith('_') | |
] | |
parent_class.__getattr__ = __getattr__ | |
parent_class.__delattr__ = __delattr__ | |
parent_class.__dir__ = __dir__ | |
for name in dir(delegate_class): | |
if hasattr(parent_class, name) or name.startswith('_'): | |
continue | |
setattr(parent_class, name, | |
delegate_property(delegate_name, name, | |
getattr(delegate_class, name).__doc__)) | |
return parent_class | |
return with_delegating_attributes |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment