public
Created

Experimenting to find a simpler approach to delegation as implemented by https://github.com/5long/forwardable

  • Download Gist
gistfile1.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
 
class AliasDelegateMethodOntoSelfApproach(object):
def __init__(self):
self.bar = set()
self.__len__, self.add = self.bar.__len__, self.bar.add
 
foo = AliasDelegateMethodOntoSelfApproach()
foo.add(1) # Delegates to foo..add(), works fine
assert foo.bar != set([0]) # negative test
assert foo.bar == set([1]) # positive test
assert len(foo) == 1
# doesn't work, why? I should have a look at the source when I have time
# it seems len requires the class to have a __len__ not just the object
# so we can't alias the bar.__len__ like this in the constructor
 
# NEW FILE - So another approach
 
# This approach has a weakness at the moment, because we don't have
# self when we create this function we cant get the function signature
# and docs, so the delegate function will be very user unfriendly -
# is there a way to fix this? metaclass? without a metaclass?
 
 
def delegate_all(attr, delegate_names):
"""Creates a list of delegate functions to bind in class body.
 
The delegate wrappers will call self.self_attr.delegate_name on the
of your object.
"""
def _delegate_wrapper_factory(delegate_name):
def delegate_wrapper(self, *args, **kwargs):
obj = getattr(self, attr)
return getattr(obj, delegate_name)(*args, **kwargs)
delegate_wrapper.__name__ = delegate_name # best I can do
return delegate_wrapper
return [_delegate_wrapper_factory(delegate_name)
for delegate_name in delegate_names]
 
 
class UseDelegateWrappersToBindOntoClassApproach(object):
def __init__(self):
self.bar = set()
 
__len__, add = delegate_all('bar',('__len__','add'))
 
foo = UseDelegateWrappersToBindOntoClassApproach()
foo.add(1) # Delegates to foo.bar.add(), works fine
assert foo.bar != set([0]) # negative test
assert foo.bar == set([1]) # positive test
assert len(foo) == 1 # also works yay
 
# NEW FILE - Cracked it? >>
 
# The last approach wasn't great because the delegate functions, don't
# look like the original - sucks - but I realized if you are doing this
# you should probably know what class your delegating to so that makes
# for quite a good solution:
 
from functools import wraps
# functools.wraps does not do a perfect job of copying the function
# signature in 2.x versions of python - solutions for that are on the
# internet - I leave it as a exercise for the perfectionists.
# I also found that set.__len__ and other such functions do not have
# a __module__ attribute so I've removed that from the things @wraps
# copies onto the wrapper function, there will be a more complete way
# to handle this I'm sure.
 
def delegate_all(attr, functions):
"""Creates a list of delegate functions to bind in class body.
 
The delegate wrappers will call function on self.attr passing
self.attr as the first argument and then all args and kwargs.
"""
def _delegate_wrapper_factory(function):
@wraps(function, assigned=('__name__','__doc__'))
def delegate_wrapper(self, *args, **kwargs):
delegate_self = getattr(self, attr)
return function(delegate_self, *args, **kwargs)
return delegate_wrapper
return [_delegate_wrapper_factory(function)
for function in functions]
 
# a delegate_one functions is straight forward.
 
# Test >>
 
class UseWrapClassMethodFromDelegateObjectApproach(object):
def __init__(self):
self.bar = set()
 
__len__, add = delegate_all('bar',(set.__len__, set.add))
 
foo = UseWrapClassMethodFromDelegateObjectApproach()
foo.add(1) # Delegates to foo.bar.add(), works fine
assert foo.bar != set([0]) # negative test
assert foo.bar == set([1]) # positive test
assert len(foo) == 1 # also works yay
# help(foo.bar) should also give useful output now :)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.