我想创建一个装饰器来描述方法并记录结果。怎么办呢?
答案 0 :(得分:60)
如果您想要进行适当的分析而不是计时,可以使用cProfile
(来自this question)的未记录的功能:
import cProfile
def profileit(func):
def wrapper(*args, **kwargs):
datafn = func.__name__ + ".profile" # Name the data file sensibly
prof = cProfile.Profile()
retval = prof.runcall(func, *args, **kwargs)
prof.dump_stats(datafn)
return retval
return wrapper
@profileit
def function_you_want_to_profile(...)
...
如果您想要更多地控制文件名,那么您将需要另一层间接:
import cProfile
def profileit(name):
def inner(func):
def wrapper(*args, **kwargs):
prof = cProfile.Profile()
retval = prof.runcall(func, *args, **kwargs)
# Note use of name from outer scope
prof.dump_stats(name)
return retval
return wrapper
return inner
@profileit("profile_for_func1_001")
def func1(...)
...
看起来很复杂,但是如果你一步一步地遵循它(并注意调用探查器的区别),它应该变得清晰。
答案 1 :(得分:10)
装饰器看起来像:
import time
import logging
def profile(func):
def wrap(*args, **kwargs):
started_at = time.time()
result = func(*args, **kwargs)
logging.info(time.time() - started_at)
return result
return wrap
@profile
def foo():
pass
无论如何,如果你想进行一些严肃的分析,我建议你使用profile或cProfile包。
答案 2 :(得分:2)
这是一个装饰器,有两个参数,配置文件输出的文件名,以及按结果排序的字段。默认值是累积时间,这对于查找瓶颈非常有用。
def profileit(prof_fname, sort_field='cumtime'):
"""
Parameters
----------
prof_fname
profile output file name
sort_field
"calls" : (((1,-1), ), "call count"),
"ncalls" : (((1,-1), ), "call count"),
"cumtime" : (((3,-1), ), "cumulative time"),
"cumulative": (((3,-1), ), "cumulative time"),
"file" : (((4, 1), ), "file name"),
"filename" : (((4, 1), ), "file name"),
"line" : (((5, 1), ), "line number"),
"module" : (((4, 1), ), "file name"),
"name" : (((6, 1), ), "function name"),
"nfl" : (((6, 1),(4, 1),(5, 1),), "name/file/line"),
"pcalls" : (((0,-1), ), "primitive call count"),
"stdname" : (((7, 1), ), "standard name"),
"time" : (((2,-1), ), "internal time"),
"tottime" : (((2,-1), ), "internal time"),
Returns
-------
None
"""
def actual_profileit(func):
def wrapper(*args, **kwargs):
prof = cProfile.Profile()
retval = prof.runcall(func, *args, **kwargs)
stat_fname = '{}.stat'.format(prof_fname)
prof.dump_stats(prof_fname)
print_profiler(prof_fname, stat_fname, sort_field)
print('dump stat in {}'.format(stat_fname))
return retval
return wrapper
return actual_profileit
def print_profiler(profile_input_fname, profile_output_fname, sort_field='cumtime'):
import pstats
with open(profile_output_fname, 'w') as f:
stats = pstats.Stats(profile_input_fname, stream=f)
stats.sort_stats(sort_field)
stats.print_stats()
答案 3 :(得分:2)
如果您已了解如何为cProfile编写装饰器,请考虑使用functools.wraps。
简单地添加一行可以帮助您更轻松地调试装饰器。如果不使用functools.wraps,装饰函数的名称将是'wrapper',并且文档字符串将丢失。
所以改进的版本将是
import cProfile
import functools
def profileit(func):
@functools.wraps(func) # <-- Changes here.
def wrapper(*args, **kwargs):
datafn = func.__name__ + ".profile" # Name the data file sensibly
prof = cProfile.Profile()
retval = prof.runcall(func, *args, **kwargs)
prof.dump_stats(datafn)
return retval
return wrapper
@profileit
def function_you_want_to_profile(...)
...
答案 4 :(得分:0)
我喜欢@detly的答案。但是有时使用 SnakeViz 查看结果是一个问题。
我做了一个略有不同的版本,将结果作为文本写入了相同的文件:
import cProfile, pstats, io
def profileit(func):
def wrapper(*args, **kwargs):
datafn = func.__name__ + ".profile" # Name the data file sensibly
prof = cProfile.Profile()
retval = prof.runcall(func, *args, **kwargs)
s = io.StringIO()
sortby = 'cumulative'
ps = pstats.Stats(prof, stream=s).sort_stats(sortby)
ps.print_stats()
with open(datafn, 'w') as perf_file:
perf_file.write(s.getvalue())
return retval
return wrapper
@profileit
def function_you_want_to_profile(...)
...
我希望这可以帮助某人...