我有一个 Python 应用程序,我想在运行时监控标准随机模块中函数的调用次数;有什么好方法可以做到这一点,还是我必须“手动”做到这一点?
5 回答
在我看来,Python 探查器应该能够“很好地”做到这一点。查看关于 SO 上的 Python 分析的帖子。
在运行时,装饰器似乎是要走的路。当然,默认的随机模块没有装饰器,因此您可能需要定义一个带有装饰器的受监视随机模块来代理随机模块并计算调用次数。如果您保持函数名称和签名与随机相同,则只需修改代码中的导入。
这是一个肮脏的解决方案:
import functools
def monitor(f):
@functools.wraps(f)
def decorated(*args, **kwargs):
print("{}: {}, {}".format(f.__name__, args, kwargs))
# Do whatever you want: increment some counter, etc.
return f(*args, **kwargs)
return decorated
import random
for name in dir(random):
f = getattr(random, name)
if callable(f) and not name.startswith('_'):
setattr(random, name, monitor(f))
此代码应在程序的最开始执行(可以在单独的模块中),在任何其他代码(包括导入)之前执行。
创建一个包装函数来增加计数,然后进行调用并返回结果。
如果您认为该“手册”,那么是的。
我能想到的最好的方法是代理——我不相信用自定义的 dict-esque 对象替换 sys.modules 是可能的。请注意,我不喜欢这个,而且你自己的头是......
class random(object):
def __init__(self):
from collections import defaultdict
import random as r_
self.R = r_
self.__lookups = defaultdict(int)
def __getattr__(self, name):
self.__lookups[name] += 1
return getattr(self.R, name)
@property
def freq(self):
return self.__lookups
一个可怕的杂物,但实际上是标准模块的代理。你如何将它放入命名空间我真的不确定 - 也许将它放入一个模块中,然后(现在开始命名)from random_testing import random
而不是正常的import random
.
我确实注意到randint
通过这种方法运行两个给我一个计数六 - 所以不完全确定内部如何在那里工作......
cProfile
返回每个函数的调用次数。
像这样的东西应该工作:
import my_module
import cProfile
cProfile.run('my_module.my_func()')
其中 my_module.my_func 是程序的入口点。这将输出以下内容:
7 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 bar.py:4(bar)
1 0.000 0.000 0.000 0.000 foo.py:4(foo)
1 0.000 0.000 0.000 0.000 my_module.py:4(my_func)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.000 0.000 0.000 0.000 {method 'random' of '_random.Random' objects}
最后一行显示,总共有 2 次调用 random 方法(在我的玩具示例中,它们位于 2 个不同的模块中,foo.py 和 bar.py)。
编辑:当然,这仅在您希望脱机计数时才有用,正如评论中所指出的那样。