I'm trying to create a method inside my class that counts the complete run of a specific function. I want to use a simple decorator. I found this reference and rewrite this simple script:
class myclass:
def __init__(self):
self.cnt = 0
def counter(function):
"""
this method counts the runtime of a function
"""
def wrapper(self, **args):
function(**args)
self.counter += 1
return wrapper
@myclass.counter
def somefunc():
print("hello from somefunc")
if __name__ == "__main__":
obj = myclass()
# or if comment @myclass.counter
# somefunc = myclass.counter(somefunc)
somefunc()
And of course, I get:
TypeError: wrapper() missing 1 required positional argument: 'self'
I tried to rewrite the counter as a class method:
class myclass:
def __init__(self):
self.cnt = 0
def counter(self, function):
"""
this method counts the runtime of a function
"""
def wrapper(**args):
function(**args)
self.cnt += 1
return wrapper
def somefunc():
print("hello from somefunc")
if __name__ == "__main__":
obj = myclass()
somefunc = obj.counter(somefunc)
for i in range(10):
somefunc()
print(obj.cnt)
Which works fine but I think it is not a valid decorator definition. Is there any way to define the decorator inside the class method and pass the self-argument to its function? or defining a decorator inside a class is useless?
EDIT:------ First I can't define the decoration outside of the class method. Second I'm trying to make a scheduled class that runs a specific function (as input) for a fixed interval and a specific amount of time so I need to count it.
So I was able to draft up something for you, below is the code:
def count(func):
def wrapper(self):
TestClass.call_count += 1
func(self)
return wrapper
class TestClass(object):
call_count = 0
@count
def hello(self):
return 'hello'
if __name__ == '__main__':
x = TestClass()
for i in range(10):
x.hello()
print(TestClass.call_count)
Why would it cause problems to have the decorator in a class:
It's not straight forward to have a decorator function
inside the class. The reasons are below:
Reason 1
Every class method must take an argument self
which is the instance
of the class through which the function is being called. Now if you make the decorator function take a self
argument, the decorator call @count
would fail as it get converted to count()
which doesn't pass the self
argument and hence the error:
TypeError: wrapper() missing 1 required positional argument: 'self'
Reason 2
Now to avoid that you can make your decorator
as static by changing the declaration like below:
@staticmethod
def count(func):
pass
But then you have another error:
TypeError: 'staticmethod' object is not callable
Which means you can't have a static method as well. If you can't have a static method in a class, you have to pass the self
instance to the method but if you pass the self
instance to it, the @count
decorator call wouldn't pass the self
instance and hence it won't work.
So here is a blog that explains it quite well, the issues associated with it and what are the alternatives.
I personally prefer the option to have a helper class
to hold all my decorators
that can be used instead of the only class in which it's defined. This would give you the flexibility to reuse the decorators instead of redefining them which would follow the ideology
code once, reuse over and over again.