如何在Python中测量经过的时间?

我想测量执行函数所需的时间。我无法让timeit工作:

import timeitstart = timeit.timeit()print("hello")end = timeit.timeit()print(end - start)
2425076 次浏览

使用#0测量两点之间经过的挂钟时间:

import time
start = time.time()print("hello")end = time.time()print(end - start)

这给出了以秒为单位的执行时间。


自Python 3.3以来的另一种选择可能是使用#0#1,具体取决于您的要求。在3.3之前,建议使用#2(感谢琥珀)。但是,它目前已被弃用:

在Unix上,将当前处理器时间作为浮点数返回以秒为单位。精确度,实际上是定义“处理器时间”的含义取决于C函数的含义同名的

在Windows上,此函数返回自第一次调用此函数,作为浮点数,基于Win32函数QueryPerformanceCounter()。分辨率通常为超过1微秒

自3.3版以来已弃用:这个函数的行为取决于在平台上:使用#0或#1代替根据您的要求,有一个定义良好的行为。

使用time.time来测量执行会给你命令的总体执行时间,包括其他进程在计算机上花费的运行时间。这是用户注意到的时间,但如果你想比较不同的代码片段/算法/函数/…

更多关于timeit的信息:

如果你想更深入地了解分析:

更新:去年我经常使用http://pythonhosted.org/line_profiler/,发现它非常有用,建议使用它而不是Pythons配置文件模块。

给定一个你想计时的函数,

test.py:

def foo():# print "hello"return "hello"

使用timeit最简单的方法是从命令行调用它:

% python -mtimeit -s'import test' 'test.foo()'1000000 loops, best of 3: 0.254 usec per loop

不要尝试使用time.timetime.clock(天真地)来比较函数的速度。

注意:不要将print语句放在要计时的函数中;否则测量的时间将取决于终端速度

python cProfile和pstats模块为测量某些函数所花费的时间提供了很好的支持,而无需在现有函数周围添加任何代码。

例如,如果你有一个python脚本timeFunctions.py:

import time
def hello():print "Hello :)"time.sleep(0.1)
def thankyou():print "Thank you!"time.sleep(0.05)
for idx in range(10):hello()
for idx in range(100):thankyou()

要运行分析器并为文件生成统计信息,您只需运行:

python -m cProfile -o timeStats.profile timeFunctions.py

这样做是使用cProfile模块来分析timeFunctions.py中的所有函数并收集timeStats.profile文件中的统计信息。请注意,我们不必向现有模块(timeFunctions.py)添加任何代码,这可以用任何模块完成。

获得stats文件后,您可以运行pstats模块,如下所示:

python -m pstats timeStats.profile

这运行交互式统计浏览器,为您提供了很多不错的功能。对于您的特定用例,您可以检查函数的统计信息。在我们的示例中,检查两个函数的统计信息向我们展示了以下内容:

Welcome to the profile statistics browser.timeStats.profile% stats hello<timestamp>    timeStats.profile
224 function calls in 6.014 seconds
Random listing order was usedList reduced from 6 to 1 due to restriction <'hello'>
ncalls  tottime  percall  cumtime  percall filename:lineno(function)10    0.000    0.000    1.001    0.100 timeFunctions.py:3(hello)
timeStats.profile% stats thankyou<timestamp>    timeStats.profile
224 function calls in 6.014 seconds
Random listing order was usedList reduced from 6 to 1 due to restriction <'thankyou'>
ncalls  tottime  percall  cumtime  percall filename:lineno(function)100    0.002    0.000    5.012    0.050 timeFunctions.py:7(thankyou)

这个虚拟的例子并没有做太多的事情,但却给你一个可以做什么的想法。这种方法最好的部分是,我不必编辑任何现有的代码来获得这些数字,显然有助于分析。

仅Python 3:

time.clock()从Python 3.3开始弃用开始,您将希望使用#1进行系统范围的计时,或者使用#2进行进程范围的计时,就像您使用time.clock()的方式一样:

import time
t = time.process_time()#do some stuffelapsed_time = time.process_time() - t

新函数process_time将不包括睡眠期间经过的时间。

使用timeit.default_timer而不是timeit.timeit。前者自动提供您的平台和Python版本上可用的最佳时钟:

from timeit import default_timer as timer
start = timer()# ...end = timer()print(end - start) # Time in seconds, e.g. 5.38091952400282

timeit.default_timer分配给time.time()或time.clock(),具体取决于操作系统。在Python 3.3上,default_timer在所有平台上都是time.perf_counter()。请参阅Python-time.clockvs.time.time()-准确性?

另见:

(仅限Ipython)您可以使用%timeit来测量平均流转时长:

def foo():print "hello"

然后:

%timeit foo()

结果是这样的:

10000 loops, best of 3: 27 µs per loop

使用上下文管理器执行此操作很有趣,它会自动记住进入with块时的开始时间,然后在块退出时冻结结束时间。只需一点技巧,您甚至可以从相同的上下文管理器函数在块内获取运行的耗时计数。

核心库没有(但可能应该有)。就位后,您可以执行以下操作:

with elapsed_timer() as elapsed:# some lengthy codeprint( "midpoint at %.2f seconds" % elapsed() )  # time so far# other lengthy code
print( "all done at %.2f seconds" % elapsed() )

下面的上下文管理器代码足以完成这个技巧:

from contextlib import contextmanagerfrom timeit import default_timer
@contextmanagerdef elapsed_timer():start = default_timer()elapser = lambda: default_timer() - startyield lambda: elapser()end = default_timer()elapser = lambda: end-start

还有一些可运行的演示代码:

import time
with elapsed_timer() as elapsed:time.sleep(1)print(elapsed())time.sleep(2)print(elapsed())time.sleep(3)

请注意,通过此函数的设计,elapsed()的返回值在块退出时被冻结,并进一步调用返回相同的持续时间(在这个玩具示例中约为6秒)。

我为此做了一个库,如果你想测量一个函数,你可以这样做

from pythonbenchmark import compare, measureimport time
a,b,c,d,e = 10,10,10,10,10something = [a,b,c,d,e]
@measuredef myFunction(something):time.sleep(0.4)
@measuredef myOptimizedFunction(something):time.sleep(0.2)
myFunction(input)myOptimizedFunction(input)

https://github.com/Karlheinzniebuhr/pythonbenchmark

在python3上:

from time import sleep, perf_counter as pct0 = pc()sleep(1)print(pc()-t0)

优雅而简短。

这是一个返回“hh: mm: ss”字符串的小计时器类:

class Timer:def __init__(self):self.start = time.time()
def restart(self):self.start = time.time()
def get_time_hhmmss(self):end = time.time()m, s = divmod(end - self.start, 60)h, m = divmod(m, 60)time_str = "%02d:%02d:%02d" % (h, m, s)return time_str

用法:

# Start timermy_timer = Timer()
# ... do something
# Get time string:time_hhmmss = my_timer.get_time_hhmmss()print("Time elapsed: %s" % time_hhmmss )
# ... use the timer againmy_timer.restart()
# ... do something
# Get time:time_hhmmss = my_timer.get_time_hhmmss()
# ... etc

我们还可以将时间转换为人类可读的时间。

import time, datetime
start = time.clock()
def num_multi1(max):result = 0for num in range(0, 1000):if (num % 3 == 0 or num % 5 == 0):result += num
print "Sum is %d " % result
num_multi1(1000)
end = time.clock()value = end - starttimestamp = datetime.datetime.fromtimestamp(value)print timestamp.strftime('%Y-%m-%d %H:%M:%S')

还有一种使用timeit的方法:

from timeit import timeit
def func():return 1 + 1
time = timeit(func, number=1)print(time)

这是另一个用于计时代码的上下文管理器-

用法:

from benchmark import benchmark
with benchmark("Test 1+1"):1+1=>Test 1+1 : 1.41e-06 seconds

或者如果你需要时间价值

with benchmark("Test 1+1") as b:1+1print(b.time)=>Test 1+1 : 7.05e-07 seconds7.05233786763e-07

benchmark.py

from timeit import default_timer as timer
class benchmark(object):
def __init__(self, msg, fmt="%0.3g"):self.msg = msgself.fmt = fmt
def __enter__(self):self.start = timer()return self
def __exit__(self, *args):t = timer() - self.startprint(("%s : " + self.fmt + " seconds") % (self.msg, t))self.time = t

改编自http://dabeaz.blogspot.fr/2010/02/context-manager-for-timing-benchmarks.html

您可以使用timeit。

下面是一个关于如何使用Python REPL测试接受参数的naive_func的示例:

>>> import timeit
>>> def naive_func(x):...     a = 0...     for i in range(a):...         a += i...     return a
>>> def wrapper(func, *args, **kwargs):...     def wrapper():...         return func(*args, **kwargs)...     return wrapper
>>> wrapped = wrapper(naive_func, 1_000)
>>> timeit.timeit(wrapped, number=1_000_000)0.4458435332577161

如果函数没有任何参数,则不需要包装函数。

使用分析器模块。它提供了非常详细的配置文件。

import profileprofile.run('main()')

它输出如下内容:

          5 function calls in 0.047 seconds
Ordered by: standard name
ncalls  tottime  percall  cumtime  percall filename:lineno(function)1    0.000    0.000    0.000    0.000 :0(exec)1    0.047    0.047    0.047    0.047 :0(setprofile)1    0.000    0.000    0.000    0.000 <string>:1(<module>)0    0.000             0.000          profile:0(profiler)1    0.000    0.000    0.047    0.047 profile:0(main())1    0.000    0.000    0.000    0.000 two_sum.py:2(twoSum)

我发现它非常有用。

我更喜欢这个。timeit文档太混乱了。

from datetime import datetime
start_time = datetime.now()
# INSERT YOUR CODE
time_elapsed = datetime.now() - start_time
print('Time elapsed (hh:mm:ss.ms) {}'.format(time_elapsed))

请注意,这里没有进行任何格式化,我只是将hh:mm:ss写入打印输出中,以便可以解释time_elapsed

这是一种非常晚的回应,但也许它对某人有目的。这是一种我认为非常干净的方式。

import time
def timed(fun, *args):s = time.time()r = fun(*args)print('{} execution took {} seconds.'.format(fun.__name__, time.time()-s))return(r)
timed(print, "Hello")

请记住,“print”是Python 3中的函数,而不是Python 2.7中的函数。但是,它适用于任何其他函数。干杯!

以下是我在阅读了许多好的答案以及其他几篇文章后的发现。

首先,如果您在timeittime.time之间进行辩论,那么timeit有两个优点:

  1. timeit选择操作系统和Python版本上可用的最佳计时器。
  2. timeit禁用垃圾回收机制,但是,这不是你可能想要或不想要的东西。

现在的问题是timeit使用起来并不那么简单,因为它需要设置,当你有一堆导入时,事情会变得很糟糕。理想情况下,你只需要一个装饰器或使用with块并测量时间。不幸的是,没有任何内置的可用功能,所以你有两个选择:

选项1:使用时间预算库

时间预算是一个通用且非常简单的库,您可以在pip安装后仅在一行代码中使用它。

@timebudget  # Record how long this function takesdef my_method():# my code

选项2:使用我的小模块

我在下面创建了一个名为timing.py的小计时实用程序模块。只需将此文件放入您的项目并开始使用它。唯一的外部依赖项是runstats,它也很小。

现在你可以通过在任何函数前面放一个装饰器来计时:

import timing
@timing.MeasureTimedef MyBigFunc():#do something time consumingfor i in range(10000):print(i)
timing.print_all_timings()

如果你想对代码部分进行计时,那么只需将其放在with块中:

import timing
#somewhere in my code
with timing.MeasureBlockTime("MyBlock"):#do something time consumingfor i in range(10000):print(i)
# rest of my code
timing.print_all_timings()

优点:

有几个半支持的版本,所以我想指出一些亮点:

  1. 由于前面描述的原因,使用timeit的计时器而不是time.time。
  2. 如果需要,您可以在计时期间禁用GC。
  3. 装饰器接受具有命名或未命名参数的函数。
  4. 能够在块计时中禁用打印(使用with timing.MeasureBlockTime() as t,然后使用t.elapsed)。
  5. 能够保持gc启用块计时。

除了ipython中的%timeit之外,您还可以将#1用于多行代码片段:

In [1]: %%timeit...: complex_func()...: 2 + 2 == 5...:...:
1 s ± 1.93 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

也可以用同样的方式在jupyter笔记本中使用,只需将魔法#0放在单元格的开头。

测量时间秒:

from timeit import default_timer as timerfrom datetime import timedelta
start = timer()
# ....# (your code runs here)# ...
end = timer()print(timedelta(seconds=end-start))

产出

0:00:01.946339

要以递归方式深入了解每个函数调用,请执行以下操作:

%load_ext snakeviz%%snakeviz

它只是在jupyter笔记本中使用那些2行代码,并生成一个很好的交互式图表。例如:

在此处输入图片描述

这是代码。同样,以%开头的2行是使用snakeviz所需的唯一额外代码行:

# !pip install snakeviz%load_ext snakevizimport globimport hashlib
%%snakeviz
files = glob.glob('*.txt')def print_files_hashed(files):for file in files:with open(file) as f:print(hashlib.md5(f.read().encode('utf-8')).hexdigest())print_files_hashed(files)

似乎也可以在笔记本电脑之外运行snakeviz。

我喜欢它简单(python 3):

from timeit import timeit
timeit(lambda: print("hello"))

单次执行的输出为微秒

2.430883963010274

补充说明:timeit默认执行匿名函数100万次,结果在中给出。因此1单执行的结果是相同的数量,但平均为微秒


对于缓慢操作,添加较低的数量迭代,否则您可能会永远等待:

import time
timeit(lambda: time.sleep(1.5), number=1)

对于总的数量次迭代,输出总是在中:

1.5015795179999714

这里有另一种方法来做到这一点:

>> from pytictoc import TicToc>> t = TicToc() # create TicToc instance>> t.tic() # Start timer>> # do something>> t.toc() # Print elapsed timeElapsed time is 2.612231 seconds.

与传统方式相比:

>> from time import time>> t1 = time()>> # do something>> t2 = time()>> elapsed = t2 - t1>> print('Elapsed time is %f seconds.' % elapsed)Elapsed time is 2.612231 seconds.

安装:

pip install pytictoc

有关更多详细信息,请参阅pypi页面

这种独特的基于类的方法提供了可打印的字符串表示、可自定义的舍入以及以字符串或浮点数形式方便地访问经过的时间。它是使用Python 3.7开发的。

import datetimeimport timeit

class Timer:"""Measure time used."""# Ref: https://stackoverflow.com/a/57931660/
def __init__(self, round_ndigits: int = 0):self._round_ndigits = round_ndigitsself._start_time = timeit.default_timer()
def __call__(self) -> float:return timeit.default_timer() - self._start_time
def __str__(self) -> str:return str(datetime.timedelta(seconds=round(self(), self._round_ndigits)))

用法:

# Setup timer>>> timer = Timer()
# Access as a string>>> print(f'Time elapsed is {timer}.')Time elapsed is 0:00:03.>>> print(f'Time elapsed is {timer}.')Time elapsed is 0:00:04.
# Access as a float>>> timer()6.841332235>>> timer()7.970274425
import time
def getElapsedTime(startTime, units):elapsedInSeconds = time.time() - startTimeif units == 'sec':return elapsedInSecondsif units == 'min':return elapsedInSeconds/60if units == 'hour':return elapsedInSeconds/(60*60)

timeit模块适合对一小段Python代码进行计时。它至少可以以三种形式使用:

1-作为命令行模块

python2 -m timeit 'for i in xrange(10): oct(i)'

2-对于短代码,将其作为参数传递。

import timeittimeit.Timer('for i in xrange(10): oct(i)').timeit()

3-对于更长的代码:

import timeitcode_to_test = """a = range(100000)b = []for i in a:b.append(i*2)"""elapsed_time = timeit.timeit(code_to_test, number=100)/100print(elapsed_time)

时间也可以用%timeit魔法函数来测量,如下所示:

%timeit -t -n 1 print("hello")

n 1仅用于运行函数1次。

测量小代码片段的执行时间。

时间单位以秒为单位作为浮点数

import timeitt = timeit.Timer('li = list(map(lambda x:x*2,[1,2,3,4,5]))')t.timeit()t.repeat()>[1.2934070999999676, 1.3335035000000062, 1.422568500000125]

重复()方法可以方便地多次调用timeit()并返回结果列表。

repeat(repeat=3)¶

有了这个列表,我们可以得到所有时间的平均值。

默认情况下,timeit()在计时期间暂时关闭垃圾回收机制。time. Timer()解决了这个问题。

优点:

timeit. Timer()使独立计时更具可比性。gc可能是被测量函数性能的重要组成部分。如果是这样,可以重新启用gc(垃圾收集器)作为设置字符串中的第一条语句。例如:

timeit.Timer('li = list(map(lambda x:x*2,[1,2,3,4,5]))',setup='gc.enable()')

python文档

如果你想方便地对函数进行计时,你可以使用一个简单的装饰器:

import time
def timing_decorator(func):def wrapper(*args, **kwargs):start = time.perf_counter()original_return_val = func(*args, **kwargs)end = time.perf_counter()print("time elapsed in ", func.__name__, ": ", end - start, sep='')return original_return_val
return wrapper

你可以在你想这样计时的函数上使用它:

@timing_decoratordef function_to_time():time.sleep(1)
function_to_time()

任何时候调用function_to_time,它都会打印它花了多长时间以及正在计时的函数的名称。

基于https://stackoverflow.com/a/30024601/5095636给出的ContextManager解决方案,以下是lambda免费版本,正如flake8根据E731对lambda的使用提出的警告:

from contextlib import contextmanagerfrom timeit import default_timer
@contextmanagerdef elapsed_timer():start_time = default_timer()
class _Timer():start = start_timeend = default_timer()duration = end - start
yield _Timer
end_time = default_timer()_Timer.end = end_time_Timer.duration = end_time - start_time

测试:

from time import sleep
with elapsed_timer() as t:print("start:", t.start)sleep(1)print("end:", t.end)
t.startt.endt.duration

计算操作持续时间的最简单方法:

import time
start_time = time.monotonic()
<operations, programs>
print('seconds: ', time.monotonic() - start_time)

官方文档这里

这是一个记录良好且完全类型提示的装饰器,我用作一般实用程序:

from functools import wrapsfrom time import perf_counterfrom typing import Any, Callable, Optional, TypeVar, cast
F = TypeVar("F", bound=Callable[..., Any])

def timer(prefix: Optional[str] = None, precision: int = 6) -> Callable[[F], F]:"""Use as a decorator to time the execution of any function.
Args:prefix: String to print before the time taken.Default is the name of the function.precision: How many decimals to include in the seconds value.
Examples:>>> @timer()... def foo(x):...     return x>>> foo(123)foo: 0.000...s123>>> @timer("Time taken: ", 2)... def foo(x):...     return x>>> foo(123)Time taken: 0.00s123
"""def decorator(func: F) -> F:@wraps(func)def wrapper(*args: Any, **kwargs: Any) -> Any:nonlocal prefixprefix = prefix if prefix is not None else f"{func.__name__}: "start = perf_counter()result = func(*args, **kwargs)end = perf_counter()print(f"{prefix}{end - start:.{precision}f}s")return resultreturn cast(F, wrapper)return decorator

示例用法:

from timer import timer

@timer(precision=9)def takes_long(x: int) -> bool:return x in (i for i in range(x + 1))

result = takes_long(10**8)print(result)

输出:

takes_long: 4.942629056sTrue

文档测试可以通过以下方式检查:

$ python3 -m doctest --verbose -o=ELLIPSIS timer.py

类型提示:

$ mypy timer.py

如何测量两次操作之间的时间。比较两次操作的时间。

import time
b = (123*321)*123t1 = time.time()
c = ((9999^123)*321)^123t2 = time.time()
print(t2-t1)

7.987022399902344 e-05

print_elapsed_time功能在下面

def print_elapsed_time(prefix=''):e_time = time.time()if not hasattr(print_elapsed_time, 's_time'):print_elapsed_time.s_time = e_timeelse:print(f'{prefix} elapsed time: {e_time - print_elapsed_time.s_time:.2f} sec')print_elapsed_time.s_time = e_time

以这种方式使用它

print_elapsed_time().... heavy jobs ...print_elapsed_time('after heavy jobs').... tons of jobs ...print_elapsed_time('after tons of jobs')

结果是

after heavy jobs elapsed time: 0.39 secafter tons of jobs elapsed time: 0.60 sec

这个函数的优点和缺点是您不需要通过启动时间

虽然在问题中没有严格要求,但通常情况下,您需要一种简单、统一的方法来增量测量几行代码之间的时间。

如果您使用的是Python 3.8或更高版本,您可以使用赋值表达式(又名海象运算符)以相当优雅的方式实现这一点:

import time
start, times = time.perf_counter(), {}
print("hello")times["print"] = -start + (start := time.perf_counter())
time.sleep(1.42)times["sleep"] = -start + (start := time.perf_counter())
a = [n**2 for n in range(10000)]times["pow"] = -start + (start := time.perf_counter())
print(times)

=>

{'print': 2.193450927734375e-05, 'sleep': 1.4210970401763916, 'power': 0.005671024322509766}

作为lambda,获取经过的时间和时间戳:

import datetimet_set = lambda: datetime.datetime.now().astimezone().replace(microsecond=0)t_diff = lambda t: str(t_set() - t)t_stamp = lambda t=None: str(t) if t else str(t_set())

在实践中:

>>>>>> t_set()datetime.datetime(2021, 3, 21, 1, 25, 17, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200), 'PDT'))>>> t = t_set()>>> t_diff(t)'0:00:14'>>> t_diff(t)'0:00:23'>>> t_stamp()'2021-03-21 01:25:57-07:00'>>> t_stamp(t)'2021-03-21 01:25:22-07:00'>>>

对于python3

如果您使用time模块,您可以获取当前时间戳,然后执行您的代码,并再次获取时间戳。现在,所花费的时间将是第一个时间戳减去第二个时间戳:

import time
first_stamp = int(round(time.time() * 1000))
# YOUR CODE GOES HEREtime.sleep(5)
second_stamp = int(round(time.time() * 1000))
# Calculate the time taken in millisecondstime_taken = second_stamp - first_stamp
# To get time in seconds:time_taken_seconds = round(time_taken / 1000)print(f'{time_taken_seconds} seconds or {time_taken} milliseconds')

您可以使用基准定时器(免责声明:我是作者):

基准定时器

使用BenchmarkTimer类来衡量执行一段代码所需的时间。
这比内置的timeit函数提供了更大的灵活性,并且与您的其余代码在相同的范围内运行。

安装

pip install git+https://github.com/michaelitvin/benchmark-timer.git@main#egg=benchmark-timer

用法

单次迭代示例

from benchmark_timer import BenchmarkTimerimport time
with BenchmarkTimer(name="MySimpleCode") as tm, tm.single_iteration():time.sleep(.3)

输出:

Benchmarking MySimpleCode...MySimpleCode benchmark: n_iters=1 avg=0.300881s std=0.000000s range=[0.300881s~0.300881s]

多次迭代示例

from benchmark_timer import BenchmarkTimerimport time
with BenchmarkTimer(name="MyTimedCode", print_iters=True) as tm:for timing_iteration in tm.iterations(n=5, warmup=2):with timing_iteration:time.sleep(.1)
print("\n===================\n")print("List of timings: ", list(tm.timings.values()))

输出:

Benchmarking MyTimedCode...[MyTimedCode] iter=0 took 0.099755s (warmup)[MyTimedCode] iter=1 took 0.100476s (warmup)[MyTimedCode] iter=2 took 0.100189s[MyTimedCode] iter=3 took 0.099900s[MyTimedCode] iter=4 took 0.100888sMyTimedCode benchmark: n_iters=3 avg=0.100326s std=0.000414s range=[0.099900s~0.100888s]
===================
List of timings:  [0.10018850000000001, 0.09990049999999995, 0.10088760000000008]

我来晚了,但是以前没有介绍过这种方法。当我们想手动基准测试某段代码时,我们可能想首先找出类中的哪个方法消耗了执行时间,而这有时并不明显。我构建了以下元类来解决这个问题:

from __future__ import annotations
from functools import wrapsfrom time import timefrom typing import Any, Callable, TypeVar, cast
F = TypeVar('F', bound=Callable[..., Any])

def timed_method(func: F, prefix: str | None = None) -> F:prefix = (prefix + ' ') if prefix else ''
@wraps(func)def inner(*args, **kwargs):  # type: ignorestart = time()try:ret = func(*args, **kwargs)except BaseException:print(f'[ERROR] {prefix}{func.__qualname__}: {time() - start}')raise        
print(f'{prefix}{func.__qualname__}: {time() - start}')return ret
return cast(F, inner)

class TimedClass(type):def __new__(cls: type[TimedClass],name: str,bases: tuple[type[type], ...],attrs: dict[str, Any],**kwargs: Any,) -> TimedClass:for name, attr in attrs.items():if isinstance(attr, (classmethod, staticmethod)):attrs[name] = type(attr)(timed_method(attr.__func__))elif isinstance(attr, property):attrs[name] = property(timed_method(attr.fget, 'get') if attr.fget is not None else None,timed_method(attr.fset, 'set') if attr.fset is not None else None,timed_method(attr.fdel, 'del') if attr.fdel is not None else None,)elif callable(attr):attrs[name] = timed_method(attr)
return super().__new__(cls, name, bases, attrs)

它允许使用如下:

class MyClass(metaclass=TimedClass):def foo(self):print('foo')    
@classmethoddef bar(cls):print('bar')    
@staticmethoddef baz():print('baz')    
@propertydef prop(self):print('prop')    
@prop.setterdef prop(self, v):print('fset')    
@prop.deleterdef prop(self):print('fdel')

c = MyClass()
c.foo()c.bar()c.baz()c.propc.prop = 2del c.prop
MyClass.bar()MyClass.baz()

它打印:

fooMyClass.foo: 1.621246337890625e-05barMyClass.bar: 4.5299530029296875e-06bazMyClass.baz: 4.291534423828125e-06propget MyClass.prop: 3.814697265625e-06fsetset MyClass.prop: 3.5762786865234375e-06fdeldel MyClass.prop: 3.5762786865234375e-06barMyClass.bar: 3.814697265625e-06bazMyClass.baz: 4.0531158447265625e-06

它可以与其他答案相结合,用更精确的答案替换time.time

以下是使用的答案:

  • 时间代码片段的简洁上下文管理器
  • time.perf_counter()来计算时间增量。应该首选它,因为它是不可调整的(系统管理员和守护程序都不能更改它的值),与time.time()相反(参见doc
  • Python 3.10+(因为打字,但可以很容易地适应以前的版本)
import timefrom contextlib import contextmanagerfrom typing import Iterator
@contextmanagerdef time_it() -> Iterator[None]:tic: float = time.perf_counter()try:yieldfinally:toc: float = time.perf_counter()print(f"Computation time = {1000*(toc - tic):.3f}ms")

一个例子如何使用它:

# Example: vector dot product computationwith time_it():A = B = range(1000000)dot = sum(a*b for a,b in zip(A,B))# Computation time = 95.353ms

附录

import time
# to check adjustabilityassert time.get_clock_info('time').adjustableassert time.get_clock_info('perf_counter').adjustable is False