入门

本页将指导您如何使用 Stopwatch 来计算程序的运行时间。

指导

当我们尝试计算一段程序的运行时间时,通常会使用 timeit 模块中定义的 default_timer 函数。

current_timer_count: float = float(timeit.default_timer())
time.sleep(3)
print(float(timeit.default_timer()) - current_timer_count)

但在一些项目中,我们可能需要统计各个阶段的运行时间并统一管理和维护。此时,上文示例代码中演示的计算方式将非常不便。当需要计算的阶段节点过多时,将会产生大量重复的代码,造成代码冗余且不利于维护。

对于 Python 入门开发者而言,上文示例代码中演示的计算方式可能不利于理解。

在下文中,我们通过几段连续的 Python 代码片段来向您演示如何使用 Stopwatch 计算程序的运行时间。

导入模块

为便于理解,我们需要使用 time 模块中的 sleep 函数来模拟程序的运行流程。

import time

在使用 Stopwatch 之前,我们应该导入 Stopwatch

from stopwatch import Stopwatch

如果您需要捕获 Stopwatch 的相关异常,应该导入:

from stopwatch import StatusError
from stopwatch import LapNameError

创建 Stopwatch

Stopwatch 的功能被定义在名为 Stopwatch 的类中。因此,我们可以通过实例化 Stopwatch 类来创建一个或多个 Stopwatch 实例。

demo_stopwatch: Stopwatch = Stopwatch(
    default_precision = None
)

Stopwatch 的构造器方法包含一个数据类型为 int 的可选参数 default_precision,该参数指示被创建的 Stopwatch 实例的计时器精度(小数点位数)。如果未提供该参数或值为 None,则该参数的默认值为 3

开始 Stopwatch

在上文中,我们定义了名为 demo_stopwatch 的变量,它是一个 Stopwatch 实例。现在我们尝试使 demo_stopwatch 开始计时。

demo_stopwatch.start()

接着,我们使用 time.sleep 函数挂起当前线程 3 秒,模拟第一阶段的程序代码运行。

time.sleep(3)

记录 Stopwatch

当我们的项目中存在多个程序阶段时,为了计算每个阶段的运行时间,阶段性计时是必不可少的。在上文示例代码中我们使用 time.sleep 函数模拟程序运行的第一个阶段,接着我们使用 lap 方法记录一次计时结果。

lap_of_watch: float = demo_stopwatch.lap(
    lap_name = 'stage1'
)

计时记录分为 2 种:

  • 命名计时记录

  • 匿名计时记录

方法 lap 具有一个数据类型为 str 的可选参数 lap_name,该参数指示计时记录的唯一名称。如果未提供该参数或值为 None,则计时记录是匿名的。

该方法返回数据类型为 float 的返回值,其指示当前阶段的计时结果。如果这是第一条计时记录,计时结果是从 Stopwatch 开始计时到目前的间隔时间(秒);否则,计时结果是从上一条计时记录到目前的间隔时间(秒)。

请注意,计时记录的名称在当前 Stopwatch 实例中必须是唯一的,即不可重复。否则,将引发 LapNameError 异常。

接着,我们再次使用 time.sleep 函数挂起当前线程 1 秒,模拟第二阶段的程序代码运行。

time.sleep(1)

然后,记录第二阶段的计时结果。

lap_of_watch: float = demo_stopwatch.lap(
    lap_name = 'stage2'
)

停止 Stopwatch

在上文中,我们使用 time.sleep 函数模拟了程序运行的第一阶段和第二阶段,并使用 lap 方法记录了程序运行第一阶段和第二阶段的计时结果。

现在,我们使 demo_stopwatch 停止计时。

total_of_watch: float = demo_stopwatch.stop()

方法 stop 返回数据类型为 float 的返回值,其指示当前 Stopwatch 实例从首次开始计时到停止计时的总计时时间(秒)。

该返回值可以被忽略,我们可以使用 get_watch 方法即时检查 Stopwatch 实例的总计时时间:

total_of_watch: float = demo_stopwatch.get_watch(
    watch_precision: int = None
)

方法 get_watch 有一个数据类型为 int 的可选参数,该参数指示获取的总计时精度(小数点)。如果未提供该参数或值为 None,该参数的默认值为 Stopwatch 类构造器方法参数 default_precision 的值。

请注意,在低于 0.1.3 版本的 Stopwatch 中使用 get_watch 方法时,应该确保 Stopwatch 实例已经停止计时,否则将引发 StatusError 异常。

我们可以使用 get_status 方法检查当前 Stopwatch 实例的状态。

if demo_stopwatch.get_status() == StopwatchStatus.Stopped:
    print('stopwatch has stopped timing')

StopwatchStatus 是预定义的 Stopwatch 状态枚举器,可以与方法 get_status 的返回值比较。

from stopwatch import StopwatchStatus

获取 Stopwatch 记录

在上文中,我们使用 lap 方法分阶段记录了程序运行第一阶段和第二阶段的计时结果。现在我们尝试获取这些计时记录。

获取命名计时记录

我们可以使用 get_lap 方法获取命名的计时记录。

lap_of_watch: float = demo_stopwatch.get_lap(
    lap_name = 'stage1', 
    lap_precision = 3
)

方法 get_lap 有 2 个参数,它们分别是:

参数 lap_precision 是可选的。如果未提供该参数或值为 None,该参数的默认值为 Stopwatch 类构造器方法参数 default_precision 的值。

请注意,如果指定计时记录名称不存在,将引发 LapNameError 异常。

如果我们需要确定一个计时记录是否存在,可以使用 has_lap 方法检查。

if not demo_stopwatch.has_lap(
    lap_name = 'stage1'
):
    print('no such lap')

获取匿名计时记录

值得注意的是,匿名计时记录并非是真正的匿名,而是使用 lap_ 和记录编号组合的方式来命名计时记录(例如:lap_1)。因此,我们也可以使用 get_lap 方法获取匿名计时记录,但更便捷的方式是使用 get_lap_by_number 方法。

lap_of_watch: float = demo_stopwatch.get_lap_by_number(
    lap_number = 1, 
    lap_precision = 3
)

与方法 get_lap 不同之处是,该方法的参数 lap_number 指示匿名记录的编号(从 1 开始),其数据类型为 int。在该方法内部仍然调用 get_lap 方法获取匿名计时记录。

请注意,如果使用该方法获取非匿名计时记录,将引发 LapNameError 异常。

为使您更加清晰的了解命名计时记录与匿名计时记录之间的关系,我们将通过下文的演示来详细说明。

demo_stopwatch.lap('stage1')
demo_stopwatch.lap()
demo_stopwatch.get_lap_by_number(1)

在上文示例代码中,我们记录了 2 次 Stopwatch 当前计时结果,命名计时记录和匿名计时记录各一条。现在 Stopwatch 实例中计时记录集的内容如下:

[
    'stage1', 
    'lap_2'
]

而在上文示例代码中,使用 get_lap_by_number 方法获取第一条命名计时记录时将引发异常:

LapNameError(no such lap: lap_1)

因为当前 Stopwatch 实例的计时记录集中不存在名为 lap_1 的命名计时记录,所以必然引发 LapNameError 异常。由此可见匿名计时记录仅是以默认形式(lap_ 和记录编号的组合)对记录命名。

获取所有计时记录的平均结果

如果我们需要获取当前 Stopwatch 实例中所有计时记录的平均计时结果,可以使用 get_average_of_laps 方法。

average_of_laps: float = demo_stopwatch.get_average_of_laps(
    average_precision = None
)

方法 get_average_of_laps 有一个数据类型为 int 的可选参数 average_precision,该参数指示获取的计时记录平均结果的精度(小数点位数)。如果未提供该参数或值为 None,该方法的行为与 get_lap 一致。

枚举所有计时记录名称

我们还可以使用 get_laps 方法枚举所有计时记录的唯一名称,其中也包含匿名计时记录。

for lap_name in demo_stopwatch.get_laps():
    print(lap_name)

方法 get_laps 返回数据类型为 list 的返回值,其中包含所有计时记录的唯一名称,成员数据类型为 str

获取计时记录条数

我们可以使用 get_lap_count 方法获取当前 Stopwatch 实例的计时记录总条数。

number_of_laps: int = demo_stopwatch.get_lap_count()

重置 Stopwatch

每个 Stopwatch 实例自开始计时起,都会产生当前实例的状态数据(例如:计时记录等...)。如果我们需要清空这些数据,则应该将 Stopwatch 实例重置为初始状态。

Tips: Stopwatch 实例可以重复开始和结束,计时记录和总计时时长都将累计,直到被重置。

demo_stopwatch.reset()

重置 Stopwatch 实例后,所有状态数据将被清理,Stopwatch 实例重置为初始状态。

请注意,如果 Stopwatch 实例仍然处于计时状态,将引发 StatusError 异常。可以使用 get_status 方法检查 Stopwatch 实例的状态。

示例

下文将以完整的 Python 示例代码演示如何使用 Stopwatch。

源代码

quickstart.py
# quickstart.py is python-3.7.4 source file

import time

from stopwatch import Stopwatch
from stopwatch import StopwatchStatus


# define main function

def main():

    # instantiate stopwatch

    demo_stopwatch: Stopwatch = Stopwatch(
        default_precision = 3   # ignorable
    )

    # make stopwatch start timing
    
    demo_stopwatch.start()

    # the first stage of the simulation program

    time.sleep(3)

    # it takes time to record the first phase of the program

    demo_stopwatch.lap('stage1')

    # the second stage of the simulation program

    time.sleep(1)

    # it takes time to record the second phase of the program

    demo_stopwatch.lap('stage2')

    # try checking the stopwatch total time every 1 second

    for count in range(3):
        print('{COUNT} check: {WATCH_SECONDS} seconds'.format(
            COUNT = count + 1, 
            WATCH_SECONDS = demo_stopwatch.get_watch()
        ))

    # record cycle check time spent on stopwatch

    demo_stopwatch.lap('stage3')

    # get the status of stopwatch

    if demo_stopwatch.get_status() == StopwatchStatus.Started:
        print('stopwatch is timing...')

    # stop and print the timing of the stopwatch
    
    print(
        (
            'total: {TOTAL_SECONDS} seconds\n'
            'stage1: {STAGE1_SECONDS} seconds\n'
            'stage2: {STAGE2_SECONDS} seconds\n'
            'stage3: {STAGE3_SECONDS} seconds\n'
            '----------\n'
            'average of stages: {AVERAGE_SECONDS} seconds'
        ).format(
            TOTAL_SECONDS = demo_stopwatch.stop(), 
            STAGE1_SECONDS = demo_stopwatch.get_lap('stage1'), 
            STAGE2_SECONDS = demo_stopwatch.get_lap('stage2'), 
            STAGE3_SECONDS = demo_stopwatch.get_lap('stage2'), 
            AVERAGE_SECONDS = demo_stopwatch.get_average_of_laps()
        )
    )

    # reset stopwatch

    demo_stopwatch.reset()


# define virtual main function

if __name__ == '__main__':
    main()

运行结果

1 check: 4.001 seconds
2 check: 4.002 seconds
3 check: 4.003 seconds
stopwatch is timing...
total: 4.006 seconds
stage1: 3.0 seconds
stage2: 1.001 seconds
stage3: 1.001 seconds
----------
average of stages: 1.335 seconds

Last updated