Created
July 3, 2013 23:06
-
-
Save tonysimpson/5923632 to your computer and use it in GitHub Desktop.
Experimenting to find a simpler approach to delegation as implemented by https://github.com/5long/forwardable
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
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