Accessing Function Attribute Created In A Decorator Outside That Decorator
Solution 1:
f.__callcount = [0]
..........
f.__callcount[0] = f.__callcount[0] + 1
......
print('fib was called a total of {0} time(s).'.format(fib.__callcount[0]))
It works. Maybe ther's something more pythonic
Solution 2:
This does what you want. I got it here- https://wiki.python.org/moin/PythonDecoratorLibrary#Alternate_Counting_function_calls
classcountcalls(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
self.__doc__ = f.func_doc
self.__name__ = f.func.func_name
def__call__(self, *args, **kwargs):
self.__numcalls += 1return self.__f(*args, **kwargs)
defcount(self):
"Return the number of times the function f was called."return countcalls.__instances[self.__f].__numcalls
@staticmethoddefcounts():
"Return a dict of {function: # of calls} for all registered functions."returndict([(f.__name__, countcalls.__instances[f].__numcalls) for f in countcalls.__instances])
@countcallsdeffib(n):
if n < 0:
raise ValueError('n must be > 0')
if n == 0or n == 1:
return1return fib(n-1) + fib(n-2)
if __name__ == '__main__':
print('Calling fib(3)...')
x = fib(3)
print('fib(3) = {0}'.format(x))
print('fib was called a total of {0} time(s).'.format(fib.count()))
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(fib.count()))
Solution 3:
The function object you are adding an attribute to is a different object than the 'original' function. Try this:
import functools
defcountcalls(f):
f.__callcount = 0 @functools.wraps(f)def_countcalls(*args, **kwds):
f.__callcount += 1print'id(f):', id(f)
print(' Called {0} time(s).'.format(f.__callcount))
return f(*args, **kwds)
return _countcalls
@countcallsdeffib(n):
"""fibinacci"""if n < 0:
raise ValueError('n must be > 0')
if n == 0or n == 1:
return1return fib(n-1) + fib(n-2)
if __name__ == '__main__':
print('Calling fib(3)...')
x = fib(3)
print'id(fib):', id(fib)
"""
>>>
Calling fib(3)...
id(f): 45611952
Called 1 time(s).
id(f): 45611952
Called 2 time(s).
id(f): 45611952
Called 3 time(s).
id(f): 45611952
Called 4 time(s).
id(f): 45611952
Called 5 time(s).
id(fib): 45612016
>>>
"""
Solution 4:
Well, here's the reason, after a bit of help. Thanks guys!
The issue is that functions are immutable. e.g.
>>>deff(func):...return func()...>>>defg():...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
defcountcalls(f):
f.__callcount = 0 @functools.wraps(f)def_countcalls(*args, **kwds):
f.__callcount += 1print(' Called {0} time(s).'.format(f.__callcount))
return f(*args, **kwds)
return f, _countcalls
deffib(n):
if n < 0:
raise ValueError('n must be > 0')
if n == 0or n == 1:
return1return 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
Post a Comment for "Accessing Function Attribute Created In A Decorator Outside That Decorator"