我想创建一个Python函数,以测试每个函数所花费的时间,并打印出其名称和时间,如何打印函数名称?如果有其他方法,请告诉我。
def measureTime(a):
start = time.clock()
a()
elapsed = time.clock()
elapsed = elapsed - start
print "Time spent in (function name) is: ", elapsed
然而,如果你想编写自己的计时方法以学习,这里提供了一个使用装饰器开始的地方。
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中),或者在Python 3中使用f.__name__
。
timing("func(s, x, d)")
无法工作。 - Peter.k在使用timeit
模块后,我不喜欢它的接口,与以下两种方法相比不够优雅。
以下代码适用于Python3。
这与@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 = ...
这一行。如果你将其改为一个函数,它将变成:
现在您想计时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)
timeit
接口并使用functools
模块中的wraps()
函数是无用的。我的意思是所有额外的代码都是不必要的。 - Billal Begueradjimport functools
。 - Guillaume Chevalier@contextmanager
def timeit_context(name, repeats=1):
startTime = time.time()
for ind in range(repeats):
yield
elapsedTime = time.time() - startTime
print('[{}] finished in {} ms'.format(name, elapsedTime * 1000./repeats))
- packoman我不明白timeit
模块有什么问题。这可能是最简单的方法。
import timeit
timeit.timeit(a, number=1)
你可以通过装饰器将函数进行封装,并且向函数中传递参数。此处有更详细的解释:http://www.pythoncentral.io/time-a-python-function/
只有需要运行函数一次并获得其返回值时,你可能会对编写自己的计时语句感兴趣。
使用timeit
模块的好处是它允许你重复多次执行。这可能是必要的,因为其他进程可能会干扰计时的准确性。因此,你应该运行它多次并查看最低值。
timeit.timeit(lambda: func(a,b,c), number=1)
?我在终端中测试假设解决方案时会使用这种方法。 - JacklynnTimeit有两个主要缺陷:它不会返回功能的返回值,并且它使用评估(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)
有一个简单的工具可以进行计时。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
希望这可以帮助到您
使用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
查看我博客上的文章:
我的做法:
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)
。
这样你就能得到答案了。
以下是使用方法:
# 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))
如果使用timeit.timeit
,则命令为
timeit.timeit(function_to_test, n=10000)
引发错误 ValueError: stmt 不是字符串也不是可调用对象
或命令
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)
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))
你可以使用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 ---
timeit
进行计时。它不是完美的,但比你尝试的要好得多,而且使用timeit
比自己编写更好的代码要容易得多。 - user395760