Reputation: 14012
I have a class with a dull repeating pattern on their functions and I wanted to turn this pattern into a decorator. But the thing is that this decorator must access some attributes of the current instance, so I wanted to turn it into a method in this class. I'm having some problems with that.
So, this is similar to what I want:
class DullRepetitiveClass:
def __init__(self, nicevariable):
self.nicevariable = nicevariable
def mydecorator(self, myfunction):
def call(*args, **kwargs):
print "Hi! The value of nicevariable is %s"%(self.nicevariable,)
return myfunction(*args, **kwargs)
return call
@mydecorator #Here, comment (1) below.
def somemethod(self, x):
return x + 1
(1) Here is the problem. I want to use the DullRepetitiveClass.mydecorator
method to decorate the somemethod
method. But I have no idea how to use the method from the current instance as the decorator.
Is there a simple way of doing this?
EDIT: Ok, the answer is quite obvious. As Sven puts it below, the decorator itself just transform the method. The method itself should deal with all things concerning the instance:
def mydecorator(method):
def call(self, *args, **kwargs):
print "Hi! The value of nicevariable is %s"%(self.nicevariable,)
return method(self, *args, **kwargs)
return call
class DullRepetitiveClass:
def __init__(self, nicevariable):
self.nicevariable = nicevariable
@mydecorator
def somemethod(self, x):
return x + 1
Upvotes: 31
Views: 6474
Reputation: 11580
Yes it can, and you can do it like this:
class DullRepetitiveClass:
def __init__(self, nicevariable):
self.nicevariable = nicevariable
def mydecorator(myfunction):
def call(self, *args, **kwargs):
self.nicevariable += 'X'
print(f"Nicevariable is now {self.nicevariable}")
return myfunction(self, *args, **kwargs)
return call
@mydecorator
def somemethod(self, x):
print(f'{x=}')
return x + 1
d = DullRepetitiveClass('start')
d.somemethod(3)
d.somemethod(4)
which produces
Nicevariable is now startX
x=3
Nicevariable is now startXX
x=4
Or you can set the decorator yourself instead of relying on the syntactic sugar provided by Python via the @
operator
class DullRepetitiveClass:
def __init__(self, nicevariable):
self.nicevariable = nicevariable
self.somemethod = self.mydecorator(self.somemethod)
def mydecorator(self, myfunction):
def call(*args, **kwargs):
self.nicevariable += 'X'
print(f"Nicevariable is now {self.nicevariable}")
return myfunction(*args, **kwargs)
return call
def somemethod(self, x):
return x + 1
Notice how call
does not get self
in its parameters (print out args
if you like to check), but the call to myfunction(*args, **kwargs)
does inject it so somemethod
gets it.
On the other hand, if you put the decorator outside the class, you could still access the object:
def mydecorator(myfunction):
def call(self, *args, **kwargs):
self.nicevariable += 'X'
print(f"Nicevariable is now {self.nicevariable}")
return myfunction(self, *args, **kwargs)
return call
class DullRepetitiveClass:
def __init__(self, nicevariable):
self.nicevariable = nicevariable
@mydecorator
def somemethod(self, x):
return x + 1
In this case self
the call
function receives self
Personally, I prefer to include the decorator within the class because it operates on its specific attributes and looks more self-contained.
Upvotes: 2
Reputation: 601489
The decorator gets only one parameter – the function or method it decorates. It does not get passed an instance as self
parameter – at the moment the decorator is called, not even the class has been created, let alone an instance of the class. The instance will be passed as first argument to the decorated function, so you should include self
as first parameter in the parameter list of call()
.
I don't see the necessity to include the decorator in the class scope. You can do this, but you can just as well have it at module scope.
Upvotes: 20