I try to use decorator pattern for class hierarchy, so I define class decorator as below:
def delegate(cls, target='_base'):
class Wrapper(object):
def __init__(self, *args, **kwargs):
self.wrapped = cls(*args, **kwargs)
def __getattr__(self, name):
if hasattr(self.wrapped, name):
return getattr(self.wrapped, name)
else:
return getattr(self.wrapped.__dict__.get(target), name)
return Wrapper
class A(object):
def __init__(self):
pass
def foo(self):
print('this is foo()')
@delegate
class B(object):
def __init__(self):
self._base = A()
def bar(self):
self.foo() # self._base.foo() will work
def main():
B().foo() #1. works
B().bar() #2. attribute not found error
Why does the call to self.foo() inside bar() doesn't go throw delegate while #1 works? To solve it with decorator without hardcode self._base.foo(), must I write method decorator for each method need _base inside B? I try to cut down the boilerplate code.
This is happening because the behavior of decorators is (at least to me) somewhat counterintuitive.
Here's what happens when you check the types of all the objects involved:
>>> type(b)
<class 'Wrapper'>
>>> type(b.wrapped)
<class 'B'>
>>> type(b.wrapped._base)
<class 'A'>
Because b
is really an instance of Wrapper
and not B
, when you call b.foo()
, what's happening is (psuedocode):
b.foo() --> Wrapper.__getattr__(foo) --> wrapped._base.foo
So foo
is coming from the Wrapper
object's __getattr__
-- not the Bar
object's.
The problem is that in defining bar
this way:
def bar(self):
self.foo()
self
does not refer to the Wrapper
object's foo
attribute, but the Bar
object's foo attribute, which doesn't exist.
So, when you call b.bar()
the method chain is:
b.bar() --> Wrapper.__getattr__(bar) --> wrapped.bar() --> wrapped.foo()
You can fix this by having Bar
invoke self._base.foo()
.