Python时间测量函数
- 2025-04-16 08:57:00
- admin 原创
- 21
问题描述:
我想创建一个 python 函数来测试每个函数所花费的时间并打印其名称及其时间,我如何打印函数名称,如果有其他方法可以做到这一点,请告诉我
def measureTime(a):
start = time.clock()
a()
elapsed = time.clock()
elapsed = elapsed - start
print "Time spent in (function name) is: ", elapsed
解决方案 1:
首先,我强烈建议使用分析器或至少使用timeit。
但是,如果您想编写自己的计时方法来严格学习,那么这里是使用装饰器开始的地方。
Python 2:
def timing(f):
def wrap(*args):
time1 = time.time()
ret = f(*args)
time2 = time.time()
print '%s function took %0.3f ms' % (f.func_name, (time2-time1)*1000.0)
return ret
return wrap
而且使用方法非常简单,只需要使用@timing装饰器即可:
@timing
def do_work():
#code
Python 3:
def timing(f):
def wrap(*args, **kwargs):
time1 = time.time()
ret = f(*args, **kwargs)
time2 = time.time()
print('{:s} function took {:.3f} ms'.format(f.__name__, (time2-time1)*1000.0))
return ret
return wrap
注意我调用它是f.func_name
为了将函数名称作为字符串(在 Python 2 中)或f.__name__
Python 3 中获取。
解决方案 2:
玩过这个timeit
模块之后,我不喜欢它的界面,与下面两种方法相比,它不那么优雅。
以下代码是 Python 3 中的。
装饰器方法
这个和@Mike的方法差不多,这里我加了个kwargs
换行符,functools
让效果更好一些。
def timeit(func):
@functools.wraps(func)
def new_func(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
elapsed_time = time.time() - start_time
print('function [{}] finished in {} ms'.format(
func.__name__, int(elapsed_time * 1_000)))
return result
return new_func
@timeit
def foobar():
mike = Person()
mike.think(30)
上下文管理器方法
from contextlib import contextmanager
@contextmanager
def timeit_context(name):
start_time = time.time()
yield
elapsed_time = time.time() - start_time
print('[{}] finished in {} ms'.format(name, int(elapsed_time * 1_000)))
例如,您可以像这样使用它:
with timeit_context('My profiling code'):
mike = Person()
mike.think()
并且区块内的代码with
将会被计时。
结论
使用第一种方法,您可以轻松注释掉装饰器以获取正常代码。但是,它只能用于计时函数。如果您不想将部分代码变成函数,则可以选择第二种方法。
例如,现在你有
images = get_images()
big_image = ImagePacker.pack(images, width=4096)
drawer.draw(big_image)
现在你需要计算这big_image = ...
条线的时长。如果将其改为函数,则计算结果如下:
images = get_images()
big_image = None
@timeit
def foobar():
nonlocal big_image
big_image = ImagePacker.pack(images, width=4096)
drawer.draw(big_image)
看起来不太好...如果你使用的是没有nonlocal
关键字的 Python 2,该怎么办?
相反,使用第二种方法非常适合这里:
images = get_images()
with timeit_context('foobar'):
big_image = ImagePacker.pack(images, width=4096)
drawer.draw(big_image)
解决方案 3:
我不明白这个timeit
模块有什么问题。这可能是最简单的方法了。
import timeit
timeit.timeit(a, number=1)
也可以向函数发送参数。你只需要用装饰器包装你的函数即可。更多解释请见:http://www.pythoncentral.io/time-a-python-function/
您可能对编写自己的计时语句感兴趣的唯一情况是,如果您只想运行一次函数并且还想获得其返回值。
使用该模块的优点 timeit
在于它允许您重复执行多次。这可能是必要的,因为其他进程可能会干扰您的计时准确性。因此,您应该多次运行该模块并查看最低值。
解决方案 4:
Timeit 有两个主要缺陷:它不返回函数的返回值,并且使用了 eval,这需要传入额外的导入设置代码。这个方法简单而优雅地解决了这两个问题:
def timed(f):
start = time.time()
ret = f()
elapsed = time.time() - start
return ret, elapsed
timed(lambda: database.foo.execute('select count(*) from source.apachelog'))
(<sqlalchemy.engine.result.ResultProxy object at 0x7fd6c20fc690>, 4.07547402381897)
解决方案 5:
有一个简单的计时工具。https ://github.com/RalphMao/PyTimer
它可以像装饰器一样工作:
from pytimer import Timer
@Timer(average=False)
def matmul(a,b, times=100):
for i in range(times):
np.dot(a,b)
输出:
matmul:0.368434
matmul:2.839355
它还可以像带有命名空间控制的插件计时器一样工作(如果您将它插入到具有大量代码并且可能在其他任何地方调用的函数中,这将很有帮助)。
timer = Timer()
def any_function():
timer.start()
for i in range(10):
timer.reset()
np.dot(np.ones((100,1000)), np.zeros((1000,500)))
timer.checkpoint('block1')
np.dot(np.ones((100,1000)), np.zeros((1000,500)))
np.dot(np.ones((100,1000)), np.zeros((1000,500)))
timer.checkpoint('block2')
np.dot(np.ones((100,1000)), np.zeros((1000,1000)))
for j in range(20):
np.dot(np.ones((100,1000)), np.zeros((1000,500)))
timer.summary()
for i in range(2):
any_function()
输出:
========Timing Summary of Default Timer========
block2:0.065062
block1:0.032529
========Timing Summary of Default Timer========
block2:0.065838
block1:0.032891
希望有帮助
解决方案 6:
使用装饰器 Python 库的装饰器方法:
import decorator
@decorator
def timing(func, *args, **kwargs):
'''Function timing wrapper
Example of using:
``@timing()``
'''
fn = '%s.%s' % (func.__module__, func.__name__)
timer = Timer()
with timer:
ret = func(*args, **kwargs)
log.info(u'%s - %0.3f sec' % (fn, timer.duration_in_seconds()))
return ret
请参阅我的博客上的帖子:
在 mobilepro.pl 博客上发表文章
我在 Google Plus 上的帖子
解决方案 7:
我的做法是:
from time import time
def printTime(start):
end = time()
duration = end - start
if duration < 60:
return "used: " + str(round(duration, 2)) + "s."
else:
mins = int(duration / 60)
secs = round(duration % 60, 2)
if mins < 60:
return "used: " + str(mins) + "m " + str(secs) + "s."
else:
hours = int(duration / 3600)
mins = mins % 60
return "used: " + str(hours) + "h " + str(mins) + "m " + str(secs) + "s."
start = time()
在执行函数/循环之前以及printTime(start)
块之后设置一个变量。
你得到了答案。
解决方案 8:
详细说明@Jonathan Ray,我认为这会更好一些
import time
import inspect
def timed(f:callable):
start = time.time()
ret = f()
elapsed = 1000*(time.time() - start)
source_code=inspect.getsource(f).strip('
')
logger.info(source_code+": "+str(elapsed)+" seconds")
return ret
它允许采用常规代码行,并将a = np.sin(np.pi)
其简单地转换为
a = timed(lambda: np.sin(np.pi))
这样就可以将时间打印到记录器上,并且您可以将结果的相同分配保留到可能需要进一步工作的变量中。
我想在 Python 3.8 中可以使用,:=
但我还没有 3.8
解决方案 9:
下面是一个 Timer 类:
易于使用:直接使用或作为装饰函数,<100 行
测量很多内容:总呼叫数、总时间、平均时间和标准差。
打印漂亮的时间
线程安全
使用方法如下:
# Create the timer
timer1 = Timer("a name", log_every=2)
# Use "with"
with timer1:
print("timer1")
# Reuse as a decorator
@timer1
def my_func():
print("my_func")
# Instantiate as a decorator
@Timer("another timer", log_every=1)
def my_func2():
print("my_func2")
my_func()
my_func2()
my_func()
以下是课程
from datetime import datetime
import time, logging, math, threading
class Timer(object):
'''A general timer class. Does not really belong in a judicata file here.'''
def __init__(self, name, log_every = 1):
self.name = name
self.log_every = 1
self.calls = 0
self.total_time = 0
self.total_squared_time = 0
self.min, self.max = None, 0
# Make timer thread-safe by storing the times in thread-local storage.
self._local = threading.local()
self._lock = threading.Lock()
def __enter__(self):
"""Start a new timer"""
self._local.start = datetime.utcnow()
def __exit__(self, exc_type, exc_val, exc_tb):
"""Stop the timer, and report the elapsed time"""
elapsed_time = (datetime.utcnow() - self._local.start).total_seconds()
with self._lock:
self.calls += 1
self.total_time += elapsed_time
if self.min == None or elapsed_time < self.min:
self.min = elapsed_time
if elapsed_time > self.max:
self.max = elapsed_time
self.total_squared_time += elapsed_time * elapsed_time
if self.log_every and (self.calls % self.log_every) == 0:
self.log()
def __call__(self, fn):
'''For use as a decorator.'''
def decorated_timer_function(*args, **kwargs):
with self:
return fn(*args, **kwargs)
return decorated_timer_function
@classmethod
def time_str(cls, secs):
if isinstance(secs, six.string_types):
try:
secs = float(secs)
except:
return "(bad time: %s)"%secs
sign = lambda x: x
if secs < 0:
secs = -secs
sign = lambda x: ("-" + x)
return sign("%d secs"%int(secs) if secs >= 120 else
"%.2f secs" % secs if secs >= 1 else
"%d ms" % int(secs * 1000) if secs >= .01 else
"%.2f ms" % (secs * 1000) if secs >= .0001 else
"%d ns" % int(secs * 1000 * 10000) if secs >= 1e-9 else
"%s" % secs)
def log(self):
if not self.calls:
logging.info("<Timer %s: no calls>"%self.name)
return
avg = 1.0 * self.total_time / self.calls
var = 1.0 * self.total_squared_time / self.calls - avg*avg
std_dev = self.time_str(math.sqrt(var))
total = self.time_str(self.total_time)
min, max, avg = [self.time_str(t) for t in [self.min, self.max, avg]]
logging.info("<Timer %s: N=%s, total=%s, avg=%s, min/max=%s/%s, std=%s>"
%(self.name, self.calls, total, avg, min, max, std_dev))
解决方案 10:
您可以timeit.default_timer
与一起使用contextmanager
:
from timeit import default_timer
from contextlib import contextmanager
@contextmanager
def timer():
start_time = default_timer()
try:
yield
finally:
print("--- %s seconds ---" % (default_timer() - start_time))
与语句一起使用with
:
def looper():
for i in range(0, 100000000):
pass
with timer():
looper()
输出:
--- 2.651526927947998 seconds ---
解决方案 11:
这是一个通用的解决方案
def timed(fn):
# make sure wherever u used this, imports will be ready
from time import perf_counter
from functools import wraps
# wraps preserves the metadata of fn
@wraps(fn)
def inner(*args, **kwargs):
start = perf_counter()
result = fn(*args, **kwargs)
end = perf_counter()
elapsed = end - start
args_ = [str(a) for a in args]
kwargs_ = ["{0}={1}".format(k, v) for (k, v) in kwargs.items()]
all_args = args_ + kwargs_
args_str = ",".join(all_args)
print("{0} ({1}) took {2:.6f} to run.".format(fn.__name__, args_str, elapsed))
return result
return inner
定义一个函数:
@timed
def sum_up(a,b):
return a+b
现在称之为:
sum_up(2,9)
解决方案 12:
timeit.timeit
对于使用if 命令的情况
timeit.timeit(function_to_test, n=10000)
引发错误ValueError: stmt is neither a string nor callable
或命令
timeit.timeit('function_to_test', n=10000)
引发错误name 'function_to_test' is not defined
,那么你需要:
替换 function_to_test
或'function_to_test'
为str(function_to_test)
,即
timeit.timeit(str(function_to_test), n=10000)
或者如果 Python 版本 >= 3.6,另一种方法是使用 f 字符串作为
timeit.timeit(f'{function_to_test}', n=10000)
关于版本使用 lambda,即timeit.timeit(lambda: function_to_test, n=10000)
,它可以工作,但是,从我的测试来看,它需要更长的时间。
这是一个具体的例子:
import timeit
def function_to_test(n):
s = 1
for i in range(n):
s += 1
return s
print("time run function_to_test: ", timeit.timeit(str(function_to_test(1000000)), number=10000))
print("time run function_to_test: ", timeit.timeit(f'{function_to_test(1000000)}', number=10000))
解决方案 13:
也许您发现此代码很有用:
import time
def start_time_measure(print_res = True):
start_time = time.time()
def end_time_measure():
elapsed_time = time.time() - start_time
if print_res:
print("Elapsed time: ", elapsed_time)
return elapsed_time
return end_time_measure
例子:
end_time_measure = start_time_measure()
# Here is some job to do
a = []
for i in range(100000):
a.append(i)
end_time_measure()
通过调用 2 个函数,可以帮助您为代码的任何部分运行计时器:start_time_measure
创建一个闭包并返回start_time_measure
可以停止计时器并打印结果 + 返回时间值的函数。
扫码咨询,免费领取项目管理大礼包!