出于教学目的,我想计算一个给定行在给定函数中执行多少次而不修改或装饰它。例如,对于函数:
def binary_search(seq, x):
(a, b) = (0, len(seq) - 1)
while a <= b:
m = (a + b) / 2
if x < seq[m]:
b = m - 1
elif x > seq[m]:
a = m + 1
else:
return m
我会写这样的东西:
print count_exec(binary_search, range(100), 44, line_number = 4)
......甚至是那样:
print count_exec(binary_search(range(100), 44), line = "m = (a + b) / 2")
...两者都应该打印执行第4行的次数(即7)。最终目标是为任何功能的复杂性提供经验方法:
我目前的解决方案是添加一个函数属性:
def binary_search(seq, x):
binary_search.count = 0 # <------------------ added
(a, b) = (0, len(seq) - 1)
while a <= b:
binary_search.count += 1 # <------------- added
m = (a + b) / 2
if x < seq[m]:
b = m - 1
elif x > seq[m]:
a = m + 1
else:
return m
binary_search(range(100), 44)
print binary_search.count
我想我可以创建一个装饰函数count_this_line
:
def binary_search(seq, x):
(a, b) = (0, len(seq) - 1)
while a <= b:
count_this_line() # <-------------------- added
m = (a + b) / 2
...
可能可以装饰函数binary_search
本身,但对我来说,这就像修改它一样。
答案 0 :(得分:2)
您可以使用line_profiler
模块执行此操作(see docs)。请注意,我必须从forked repo获得3.x兼容版本 - 不确定它是否已合并。
例如。我将二进制搜索功能放在一个文件中,然后添加:
prof = profile(binary_search)
prof(range(100), 44)
这与文档中提到的@profile
装饰器相同,但您不必修改原始代码。我跑了
kernprof.py -l binsearch.py
python -m line_profiler binsearch.py.lprof
然后突然出现:
Function: binary_search at line 1
Total time: 4.1e-05 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 def binary_search(seq, x):
2 1 6 6.0 14.6 (a, b) = (0, len(seq) - 1)
3 7 8 1.1 19.5 while a <= b:
4 7 7 1.0 17.1 m = (a + b) // 2
5 7 8 1.1 19.5 if x < seq[m]:
6 2 2 1.0 4.9 b = m - 1
7 5 5 1.0 12.2 elif x > seq[m]:
8 4 4 1.0 9.8 a = m + 1
9 else:
10 1 1 1.0 2.4 return m
&#34;命中&#34;是你正在寻找的号码。作为奖励,您也可以获得时间信息,但是对于许多执行来说这会更准确。
答案 1 :(得分:2)
根据Jason的建议,我编写了一个纯Python解决方案:
import line_profiler
import __builtin__
import cStringIO
import re
def profile(path, function_call, line_number):
prof = line_profiler.LineProfiler()
__builtin__.__dict__['profile'] = prof
script = open(path).read()
ns = locals()
function_name = function_call[:function_call.index("(")]
rex = re.compile("((?ms)^def %s.+)" % function_name)
script = rex.sub(r"@profile\n\1\n%s" % function_call, script)
exec(script, ns, ns)
stream = cStringIO.StringIO()
prof.print_stats(stream)
s = stream.getvalue()
stream.close()
return int(re.search(r"(?m)^\s*%s\s*(\S*)" % (line_number+1), s).group(1))
if __name__ == "__main__":
print profile("binary_search.py", "binary_search(range(100), 44)", 3)
它读取包含要分析的函数的脚本的源,装饰这个,将所需的调用追加到末尾,执行它,将统计信息转储到字符串中,提取给定行号的命中数,以及将其作为int
返回。它可以按要求运行,但会带来重要的性能损失。
也许更好的解决方案是删除分析器,但保持动态装饰和执行源代码的想法。如果植入它,我会编辑我的答案。
无论如何,谢谢Jason为我提供了一条出路!