# 入门

## 指导 <a href="#guide" id="guide"></a>

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

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

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

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

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

### 导入模块 <a href="#import-module" id="import-module"></a>

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

```python
import time
```

在使用 Stopwatch 之前，我们应该导入 `Stopwatch`：

```python
from stopwatch import Stopwatch
```

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

```python
from stopwatch import StatusError
from stopwatch import LapNameError
```

### 创建 Stopwatch <a href="#create-stopwatch" id="create-stopwatch"></a>

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

```python
demo_stopwatch: Stopwatch = Stopwatch(
    default_precision = None
)
```

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

### 开始 Stopwatch <a href="#start-stopwatch" id="start-stopwatch"></a>

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

```python
demo_stopwatch.start()
```

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

```python
time.sleep(3)
```

### 记录 Stopwatch <a href="#lap-stopwatch" id="lap-stopwatch"></a>

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

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

计时记录分为 2 种：

* 命名计时记录
* 匿名计时记录

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

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

{% hint style="danger" %}
请注意，计时记录的名称在当前 Stopwatch 实例中必须是唯一的，即不可重复。否则，将引发 LapNameError 异常。
{% endhint %}

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

```python
time.sleep(1)
```

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

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

### 停止 Stopwatch <a href="#stop-stopwatch" id="stop-stopwatch"></a>

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

现在，我们使 `demo_stopwatch` 停止计时。

```python
total_of_watch: float = demo_stopwatch.stop()
```

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

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

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

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

{% hint style="danger" %}
请注意，在低于 0.1.3 版本的 Stopwatch 中使用 get\_watch 方法时，应该确保 Stopwatch 实例已经停止计时，否则将引发 StatusError 异常。
{% endhint %}

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

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

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

```python
from stopwatch import StopwatchStatus
```

### 获取 Stopwatch 记录 <a href="#get-laps" id="get-laps"></a>

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

#### 获取命名计时记录

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

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

方法 `get_lap` 有 2 个参数，它们分别是：

| 参数名            | 数据类型 | 可选    | 描述               |
| -------------- | ---- | ----- | ---------------- |
| lap\_name      | str  | False | 计时记录的唯一名称。       |
| lap\_precision | int  | True  | 获取的计时记录精度（小数点位数） |

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

{% hint style="danger" %}
请注意，如果指定计时记录名称不存在，将引发 LapNameError 异常。
{% endhint %}

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

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

#### 获取匿名计时记录

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

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

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

{% hint style="danger" %}
请注意，如果使用该方法获取非匿名计时记录，将引发 LapNameError 异常。
{% endhint %}

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

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

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

```python
[
    'stage1', 
    'lap_2'
]
```

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

```
LapNameError(no such lap: lap_1)
```

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

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

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

```python
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` 方法枚举所有计时记录的唯一名称，其中也包含匿名计时记录。

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

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

#### 获取计时记录条数

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

```python
number_of_laps: int = demo_stopwatch.get_lap_count()
```

### 重置 Stopwatch <a href="#reset-stopwatch" id="reset-stopwatch"></a>

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

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

```python
demo_stopwatch.reset()
```

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

{% hint style="danger" %}
请注意，如果 Stopwatch 实例仍然处于计时状态，将引发 StatusError 异常。可以使用 get\_status 方法检查 Stopwatch 实例的状态。
{% endhint %}

## 示例 <a href="#demo" id="demo"></a>

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

### 源代码 <a href="#source" id="source"></a>

{% code title="quickstart.py" %}

```python
# 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()
```

{% endcode %}

### 运行结果 <a href="#result" id="result"></a>

```bash
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
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://smallso.gitbook.io/stopwatch/python/getting-started.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
