Assume I have some simple class
class TestClass:
def doSomething(self):
print 'Did something'
I would like to decorate the doSomething
method, for example to count the number of calls
class SimpleDecorator(object):
def __init__(self,func):
self.func=func
self.count=0
def __get__(self,obj,objtype=None):
return MethodType(self,obj,objtype)
def __call__(self,*args,**kwargs):
self.count+=1
return self.func(*args,**kwargs)
Now this counts the number of calls to the decorated method, however I would like to have per-instance counter, such that after
foo1=TestClass()
foo1.doSomething()
foo2=TestClass()
foo1.doSomething.count
is 1 and foo2.doSomething.count
is 0. From what I understand, this is not possible using decorators. Is there some way to achieve such behaviour?
To decorate a method in a class, first use the '@' symbol followed by the name of the decorator function. A decorator is simply a function that takes a function as an argument and returns yet another function.
Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together. It is also known as nested decorators in Python.
A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.
To decorate a function with a class, we must use the @ syntax followed by our class name above the function definition. Following convention, we will use camel case for our class name. In the class definition, we define two methods — the init constructor and the magic (or dunder) call method.
Utilize the fact that self
(i.e. the object which the method is invoked on) is passed as a parameter to the method:
import functools
def counted(method):
@functools.wraps(method)
def wrapped(obj, *args, **kwargs):
if hasattr(obj, 'count'):
obj.count += 1
else:
obj.count = 1
return method(obj, *args, **kwargs)
return wrapped
In above code, we intercept the object as obj
parameter of the decorated version of method. Usage of the decorator is pretty straightforward:
class Foo(object):
@counted
def do_something(self): pass
Wouldn't the first element of *args
be the object the method is being invoked on? Can't you just store the count there?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With