Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

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

View gist:5923632
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 :)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.