I am new to python decorators. I have understood the basic concepts with the help of simple examples. But when I tried to read this more practical decorator, I feel lost. Given below is the code followed by my questions:
class countcalls(object):
"Decorator that keeps track of the number of times a function is called."
__instances = {}
def __init__(self, f):
self.__f = f
self.__numcalls = 0
countcalls.__instances[f] = self
def __call__(self, *args, **kwargs):
self.__numcalls += 1
return self.__f(*args, **kwargs)
def count(self):
"Return the number of times the function f was called."
return countcalls.__instances[self.__f].__numcalls
@countcalls
def f():
print 'f called'
f()
f()
f()
print f.count() # prints 3
My doubts:
-
When we prefix the decorator to a function, does that mean that we are creating an object of the decorator class right there? In our case, when it says:
@countcalls
def f():
print 'f called'
Is @countcalls equivalent to creating a countcalls object and passing the function below to its __init__ method?
-
The
__call__is taking three arguments.selfis fine as far as the question above is answered. What the hell are the two other arguments:*args, **kwargsand what are they achieving? -
How can I get better at decorators?
This code seems to have some oddness. Let’s talk about the slightly-simpler code
Remember
is the same thing as
so when we use the decorator, the function is stored using the
_fattribute.So
fis acountcallsinstance. When you dof(...)you callf.__call__(...)—that’s how you implement()syntax for your own instances. So when you callf, what happens?First, you use
*argsand**kwargs, which in the definition condense all positional and keyword arguments into a tuple and dict, and later in the call expand a sequence and a dict into arguments (see 4.7.4 in the official tutorial for more information). Here’s a partial exampleso
def f(*args, **kwargs): return g(*args, **kwargs)just does a passthrough on all arguments.Aside from that, you’re just keeping track of how many times you’ve been in
__call__for this instance (how many times you’ve calledf).Just remember that
@decdef f(...): ...is the same asdef f(...): ...f = dec(f)and you should be able to figure out most decorators fine, given enough time. Like all things, practice will help you do this quicker and easier.