Reputation: 10257
Let's assume I have a class to be used as a decorator like such:
class specialmethod:
def __init__(self,func):
self.func = func
def __call__(self,arg1,arg2):
print arg1,arg2
self.func(arg1,arg2)
But the method being decorated is within another class, that also must utilize self
(the values within self are relevant to the function body post-decoration)
class A:
@specialmethod
def dosomething(self,data,otherdata):
#this guy down here isn't getting the right value for self, an instance of A
print type(self)
Traceback (most recent call last):
File "deco.py", line 18, in <module>
a.dosomething('foo','bar')
File "deco.py", line 7, in __call__
self.func(arg1,arg2)
TypeError: dosomething() takes exactly 3 arguments (2 given)
Is there anyway to preserve the way self
would work if it were not decorated? I've tried passing it explicitly but it still expects the wrong number of parameters.
EDIT
I should clarify that the intended value for self in the final function body of dosomething
should refer to the instance of A
... not looking to get the decorator instance into the decorated function.
Upvotes: 3
Views: 1655
Reputation: 133415
When you use a class like a decorator, you have to realize that the normal magic that turns functions into methods (the descriptor protocol, specifically the __get__
method) won't work for your class unless you explicitly implement it. You could do this manually, by implementing a __get__
method that returns a new type that remembers the instance it was retrieved from as well as the actual callable to call, but it's a lot of work for very little gain.
A more common approach is to not use an actual class as a decorator, and always have the decorator return an actual Python function. In this particular case, there isn't really a point in keeping the class at all, so you could just do:
def specialmethod(f):
@functools.wraps(f)
def wrapper(arg1, arg2):
print arg1, arg2
f(arg1, arg2)
return wrapper
... but if you really want to keep the class, you can do so by doing something like:
class _specialmethod:
def __init__(self,func):
self.func = func
def __call__(self,arg1,arg2):
print arg1,arg2
self.func(arg1,arg2)
def specialmethod(f):
wrapper_class = _specialmethod(f)
@functools.wraps(f)
def wrapper(arg1, arg2):
wrapper_class(arg1, arg2)
return wrapper
Upvotes: 4