我想测量执行一个函数所花费的时间。我没时间工作:

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

当前回答

我参加聚会已经很晚了,但这种方法以前没有涉及过。当我们想要手动对某段代码进行基准测试时,我们可能需要首先找出哪些类方法占用了执行时间,这有时并不明显。我构建了以下元类来解决这个问题:

from __future__ import annotations

from functools import wraps
from time import time
from 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: ignore
        start = 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')
    
    @classmethod
    def bar(cls): 
        print('bar')
    
    @staticmethod
    def baz(): 
        print('baz')
    
    @property
    def prop(self): 
        print('prop')
    
    @prop.setter
    def prop(self, v): 
        print('fset')
    
    @prop.deleter
    def prop(self): 
        print('fdel')


c = MyClass()

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

MyClass.bar()
MyClass.baz()

它打印:

foo
MyClass.foo: 1.621246337890625e-05
bar
MyClass.bar: 4.5299530029296875e-06
baz
MyClass.baz: 4.291534423828125e-06
prop
get MyClass.prop: 3.814697265625e-06
fset
set MyClass.prop: 3.5762786865234375e-06
fdel
del MyClass.prop: 3.5762786865234375e-06
bar
MyClass.bar: 3.814697265625e-06
baz
MyClass.baz: 4.0531158447265625e-06

它可以与其他答案相结合,以更精确的方式代替time.time。

其他回答

使用探查器模块。它提供了非常详细的概况。

import profile
profile.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)

我发现它很有启发性。

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_time
    else:
        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 sec
after tons of jobs elapsed time: 0.60 sec  

这个函数的优点和缺点是你不需要经过开始时间

以下是一个答案,使用:

对代码片段进行计时的简洁上下文管理器time.perf_counter()计算时间增量。与time.time()相反,它是不可调整的(sysadmin和守护程序都不能更改其值),因此应该首选它(参见文档)python3.10+(因为键入,但可以很容易地适应以前的版本)

import time
from contextlib import contextmanager
from typing import Iterator

@contextmanager
def time_it() -> Iterator[None]:
    tic: float = time.perf_counter()
    try:
        yield
    finally:
        toc: float = time.perf_counter()
        print(f"Computation time = {1000*(toc - tic):.3f}ms")

如何使用它的示例:

# Example: vector dot product computation
with 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 adjustability
assert time.get_clock_info('time').adjustable
assert time.get_clock_info('perf_counter').adjustable is False

基于https://stackoverflow.com/a/30024601/5095636,以下为无lambda版本,如flake8根据E731对lambda使用的警告:

from contextlib import contextmanager
from timeit import default_timer

@contextmanager
def elapsed_timer():
    start_time = default_timer()

    class _Timer():
      start = start_time
      end = 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.start
t.end
t.duration

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

用法:

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+1
print(b.time)
=>
Test 1+1 : 7.05e-07 seconds
7.05233786763e-07

基准.py:

from timeit import default_timer as timer

class benchmark(object):

    def __init__(self, msg, fmt="%0.3g"):
        self.msg = msg
        self.fmt = fmt

    def __enter__(self):
        self.start = timer()
        return self

    def __exit__(self, *args):
        t = timer() - self.start
        print(("%s : " + self.fmt + " seconds") % (self.msg, t))
        self.time = t

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