Let’s start with the definition of f
:
@fcount
def f(n):
return n+2
This defines a f
as the return value of a call to the function fcount
, which is used as a decorator (the leading @
) here.
This code is roughly equivalent with
def f(n):
return n+2
f = fcount(f)
since the decorator – fcount
– does not return anything, f
is None
and not a function at the call site.
In your case fcount
should return some function and add a count
attribute to that returned function. Something useful (?) might be
def fcount(fn):
def wrapper(n):
wrapper.count += 1
return fn(n)
wrapper.count = 0
return wrapper
EDIT
As @jonrsharpe pointed out, a generalized decorator can forward positional and keyword arguments by capturing them with *args
and **kwargs
in the signature and expanding them in the same way when calling another function. The names args
and kwargs
are used by convention.
Python also has a helper function (a decorator itself) that can transfer information (name, docstring and signature information) from one function to another: functools.wraps
. A complete example looks like this:
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator
def f(a, b, c=None):
"The useful f function"
pass
print f.__name__ # `f` rather than `wrapper`
print help(f) # `f(*args, **kwargs) The useful f function` rather than `wrapper(*args, **kwargs)`