[英]Accessing function attribute created in a decorator outside that decorator
我想计算给定函数被调用的次数。
因此,我做了一个countcalls
装饰器,为我的函数提供__callcount
属性,该属性在每次调用时都会递增。 很简单。
我的问题是稍后收回__callcount
值。
这是我的代码:
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 _countcalls
@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__':
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(fib.__callcount))
生成以下输出(Python v3.3.0):
Calling fib(3)...
Called 1 time(s).
Called 2 time(s).
Called 3 time(s).
Called 4 time(s).
Called 5 time(s).
fib(3) = 3
Calling fib(3) again...
Called 6 time(s).
Called 7 time(s).
Called 8 time(s).
Called 9 time(s).
Called 10 time(s).
fib(3) = 3
fib was called a total of 0 time(s).
为什么fib.__callcount
在最后一行等于0
? 如输出所示, __callcount
递增,并在fib
调用之间持续存在。
我想念什么?
f.__callcount = [0]
..........
f.__callcount[0] = f.__callcount[0] + 1
......
print('fib was called a total of {0} time(s).'.format(fib.__callcount[0]))
有用。
也许还有一些Pythonic
这就是您想要的。 我在这里-https : //wiki.python.org/moin/PythonDecoratorLibrary#Alternate_Counting_function_calls
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
self.__doc__ = f.func_doc
self.__name__ = f.func.func_name
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
@staticmethod
def counts():
"Return a dict of {function: # of calls} for all registered functions."
return dict([(f.__name__, countcalls.__instances[f].__numcalls) for f in countcalls.__instances])
@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__':
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()))
要向其添加属性的功能对象与“原始”功能不同。 尝试这个:
import functools
def countcalls(f):
f.__callcount = 0
@functools.wraps(f)
def _countcalls(*args, **kwds):
f.__callcount += 1
print 'id(f):', id(f)
print(' Called {0} time(s).'.format(f.__callcount))
return f(*args, **kwds)
return _countcalls
@countcalls
def fib(n):
"""fibinacci"""
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__':
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
>>>
"""
好吧,这是经过一点帮助的原因。 多谢你们!
问题是功能是不可变的。 例如
>>> def f(func):
... return func()
...
>>> def g():
... return 'sunflower seeds'
...
>>> id(g)
139636515497336
>>> g = f(g)
>>> id(g)
139636515515112
因此,要获得该功能的唯一途径f
我们分配__callcount
中的定义属性countcalls
是从返回功能callcount
。 但是我们已经在返回内部函数_countcalls
。 我们可以同时返回f
和_countcalls
但这会弄乱@countcalls
装饰器语法。
您仍然可以用这种方式来做,只是不那么漂亮。
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))
长话短说,只需使用Python Decorator Library中的类 。 :D
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.