Well, here's the reason, after a bit of help. Thanks guys!
The issue is that functions are immutable. e.g.
>>> def f(func):
... return func()
...
>>> def g():
... return 'sunflower seeds'
...
>>> id(g)
139636515497336
>>> g = f(g)
>>> id(g)
139636515515112
So, the only way to get the function f
we assigned the __callcount
attribute to in the definition of countcalls
is to return that function from callcount
. But we're already returning the inner function _countcalls
. We can return both f
and _countcalls
but that messes up the @countcalls
decorator syntax.
You can still do it this way, it's just not as pretty.
import functools
def countcalls(f):
f.__callcount = 0
@functools.wraps(f)
def _countcalls(*args, **kwds):
f.__callcount += 1
print(' Called {0} time(s).'.format(f.__callcount))
return f(*args, **kwds)
return f, _countcalls
def fib(n):
if n < 0:
raise ValueError('n must be > 0')
if n == 0 or n == 1:
return 1
return fib(n-1) + fib(n-2)
if __name__ == '__main__':
counter, fib = countcalls(fib)
print('Calling fib(3)...')
x = fib(3)
print('fib(3) = {0}'.format(x))
print('Calling fib(3) again...')
x = fib(3)
print('fib(3) = {0}'.format(x))
print('fib was called a total of {0} time(s).'.format(counter.__callcount))
Long story short, just use the class from the Python Decorator Library. :D