让我们假设一个简单的方法:
ST_SetSRID(ST_point(x, y), 12345)
要使用装饰器计时此方法,一个简单的装饰器将是:
def test_method():
a = 1
b = 10000
c = 20000
sum1 = sum(range(a,b))
sum2 = sum(range(b,c))
return (sum1,sum2)
现在,如果我想计算from functools import wraps
def timed_decorator(f):
@wraps(f)
def wrapper(*args, **kwds):
start = time.time()
result = f(*args, **kwds)
elapsed = (time.time() - start)*1000
logger.debug("f::{0} t::{1:0.2f} ms".format(f.__name__, elapsed))
return result
return wrapper
第4行test_method
的特定行,那么当前的实现涉及内联编码,如:
sum1 = sum(range(a,b))
目的是使用装饰器来定时特定方法的行M到N而不修改方法中的代码。 是否可以使用装饰器注入这样的逻辑?
答案 0 :(得分:11)
您可以使用上下文管理器。
import contextlib
@contextlib.contextmanager
def time_measure(ident):
tstart = time.time()
yield
elapsed = time.time() - tstart
logger.debug("{0}: {1} ms".format(ident, elapsed))
在您的代码中,您可以像
一样使用它with time_measure('test_method:sum1'):
sum1 = sum(range(a, b))
顺便说一句,如果您想改进代码,可以使用高斯求和公式(解释为here)而不是sum(range(a, b))
。
def sum_range(a, b):
r_a = (a ** 2 + a) / 2 - a
r_b = (b ** 2 + b) / 2 - b
return r_b - r_a
答案 1 :(得分:1)
我能想到的一种方法是在跟踪器函数中处理“line”事件时使用sys.settrace()并记录时间。但有一点需要注意的是,设置跟踪器的做法可能会导致记录的时间不准确。
一般的想法是:
FLN = inspect.currentframe().f_lineno.
代码:
import sys
from functools import wraps
import time
import linecache
_func_name_ = None
_func_ln_ = 0
_start_ = 0
_end_ = 0
_timestamp_ = 0
def trace_calls(frame, event, arg):
global _func_name_, _func_ln_
def trace_lines(frame, event, arg):
global _timestamp_
if event != 'line':
return
line_no = frame.f_lineno
filename = frame.f_code.co_filename
if line_no-_func_ln_ == _start_:
_timestamp_ = time.time()
print "%d %s TS:%d"%(line_no, linecache.getline(filename, line_no)[:-1], _timestamp_)
elif line_no-_func_ln_ == _end_:
_timestamp_ = time.time() - _timestamp_
print "%d %s"%(line_no, linecache.getline(filename, line_no)[:-1])
print "Lines %d to %d of %s takes %d seconds."%(_start_, _end_, _func_name_, _timestamp_)
if event != 'call':
return
co = frame.f_code
_func_ln_ = frame.f_lineno # record the line number at function entry point
func_name = co.co_name
if func_name != _func_name_:
return
return trace_lines
def time_lines(start, end):
global _start_, _end_
_start_, _end_ = start+1, end+2 # function name takes a line, end is inclusive
def inner(f):
@wraps(f)
def wrapper(*args, **kwargs):
global _func_name_
_func_name_ = f.__name__
sys.settrace(trace_calls)
f(*args, **kwargs)
sys.settrace(None)
return wrapper
return inner
@time_lines(2,4)
def tested_func():
print "Enter target function"
time.sleep(2)
time.sleep(1)
time.sleep(3)
print "Exit target function"
if __name__=="__main__":
tested_func()
答案 2 :(得分:1)
它非常难看,而且代码不是很稳定。但我发现执行此任务的唯一方法是在注入代码后再次执行该函数的代码 像这样:
import inspect
import re
import time
def inject_timer(f,n,m):
codelines = inspect.getsourcelines(f)[0]
ident_lvl = re.search("^[ \t]*",codelines[n]).group(0)
codelines.insert(n,ident_lvl + "start_longJibrishTo_preventCollision = time.time()\n")
codelines.insert(m+2,ident_lvl + "elapsed_longJibrishTo_preventCollision = (time.time() - start_longJibrishTo_preventCollision)*1000\n")
codelines.insert(m+3,ident_lvl + """print("f::{0} t::{1:0.2f} ms".format("""+f.__name__+""", elapsed_longJibrishTo_preventCollision))\n""")
#print "".join(codelines)
exec "".join(codelines) in globals()
def test_method():
a = 1
b = 10000
time.sleep(2)
c = 20000
sum1 = sum(range(a,b))
sum2 = sum(range(b,c))
return (sum1,sum2)
inject_timer(test_method,3,5)
答案 3 :(得分:0)
装饰器只能装饰callables(例如函数,方法,类)。只要您不将它们包装在自己的可调用对象中,单行或一组行就不可调用。
对于代码的单位计时,您应该选择适当的重复次数。目标是确保执行时间长于几微秒或几毫秒,否则测量误差将太大。
你看过timeit
模块了吗?
答案 4 :(得分:0)
这里是@NiklasR的答案的略有变化,其中没有logger
,但有print
,还有一个现成的示例:
import contextlib, time
@contextlib.contextmanager
def time_measure(ident):
tstart = time.time()
yield
elapsed = time.time() - tstart
print("{0}: {1} ms".format(ident, elapsed))
with time_measure('hello'):
sum1 = sum(x ** 2 for x in range(1, 1000000))
# hello: 0.577033281326294 ms