I\'m writing a small framework for orchestrating AWS clusters and there are some common hierarchical patterns that appear over and over again. One such pattern is gathering a co
I've been researching on this and found two solutions. Use a decorator to change the class and create the delegators, or using descriptors for the delegators. I started with the first and then evolve to the second which I like more, so I will start by it. Both can be found here: https://gist.github.com/dhilst/7435a09b4419da349bb4cc4ae855a451 with doctests :)
-- Edit --
For anybody interested I made this a library: https://pypi.org/project/delegateto/
There were bugs in gist implementation, people contributed to this on github, the pypi project is updated, gist not. I strongly recommend you to use pypi version.
Descriptors are things that can be getted and setted. In this case we are interested in the gettable ability of descriptors. The delegate descriptor defined like this
class DelegateTo:
def __init__(self, to, method=None):
self.to = to
self.method = method
def __get__(self, obj, objecttype):
if self.method is not None:
return getattr(getattr(obj, self.to), self.method)
for method, v in obj.__class__.__dict__.items():
if v is self:
self.method = method
return getattr(getattr(obj, self.to), method)
And is used like this
class Foo:
upper = DelegateTo('v')
__len__ = DelegateTo('l')
__iter__ = DelegateTo('l')
def __init__(self, v, l):
self.v = v
self.l = l
To call a descriptor simply call the method Foo('hello').upper()
. Magic methods also works len(Foo('', [1,2,3,4]))
returns 4. The gist link above has a more powerful implementation but the basics is the same.
Everytime that you need to change a class behavior in an repetitive way, a decorator is a candidate. In this case the decorator will call setattr
at the class to create the delegators.
def delegate(to, *methods):
def dec(klass):
def create_delegator(method):
def delegator(self, *args, **kwargs):
obj = getattr(self, to)
m = getattr(obj, method)
return m(*args, **kwargs)
return delegator
for m in methods:
setattr(klass, m, create_delegator(m))
return klass
return dec
The usage is also simple, just decorate the class, as many times you want. The decorator will modify the class inplace so the same class is returned.
Here is a usage
@delegate('v', 'upper', 'lower')
class Foo:
def __init__(self, v):
self.v = v
And the call of the delegated method is also transparent Foo('hello').upper()
. I prefer the second one because it seems more idiomatic for me. The decorator has an advantage to support multiple methods but this is implementable on the descriptor form too.
Again, I really recommend that you see the gist: https://gist.github.com/dhilst/7435a09b4419da349bb4cc4ae855a451 there are tons of examples in the docstring. Just modify them and execute the scripts to play around.
-- Edit --
For any body interested, I make this a pip package https://pypi.org/project/delegateto/
-- Edit --
There were bugs in gist implementation, people contributed to this on github, the pypi project is updated, gist not. I strongly recommend you to use pypi version.
Regards