# Getting Started

## Guide

When we try to calculate the runtime of a program, we usually use the `default_timer` function defined in the `timeit` module.

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

However, in some projects, we may need to count the running time of each phase and manage and maintain it. At this point, the calculations demonstrated in the sample code above will be very inconvenient. When there are too many phase nodes that need to be calculated, a lot of duplicate code will be generated, resulting in code redundancy and unfavorable maintenance.

For Python introductors, the calculations demonstrated in the sample code above may not be good for understanding.

In the following, we show you how to use Stopwatch to calculate the running time of a program through several consecutive pieces of Python code.

### Import module

For the sake of understanding, we need to use the `sleep` function in the `time` module to simulate the running process of the program.

```python
import time
```

Before using Stopwatch, we should import `Stopwatch`:

```python
from stopwatch import Stopwatch
```

If you need to catch a related exception for Stopwatch, you should import:

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

### Create Stopwatch

The functionality of Stopwatch is defined in a class called `Stopwatch`. Therefore, we can create one or more Stopwatch instances by instantiating the `Stopwatch` class.

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

The constructor method of class `Stopwatch` contains an optional parameter `default_precision` of data type `int` that indicates the timer precision (number of decimal places) of the Stopwatch instance being created. If the parameter is not supplied or the value is `None`, the default value for this parameter is `3`.

### Start Stopwatch

In the above, we defined a variable called `demo_stopwatch`, which is a Stopwatch instance. Now let's try to make the `demo_stopwatch` start timing.

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

Next, we use the `time.sleep` function to suspend the current thread for 3 seconds, simulating the first phase of the program code to run.

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

### Record Stopwatch

When there are multiple program phases in our project, phased timing is essential in order to calculate the runtime of each phase. In the sample code above we used the `time.sleep` function to simulate the first phase of the program run, then we used the `lap` method to record the timing results.

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

There are 2 types of timing records:&#x20;

* Named timing record
* Anonymous timing record

The method `lap` has an optional parameter `lap_name` of data type `str`, which indicates the unique name of the timed record. If this parameter is not supplied or the value is `None`, the timing record is anonymous.

This method returns a return value of data type `float` indicating the timing result of the current phase. If this is the first timing record, the timing is the time from the Stopwatch to the current interval (in seconds); otherwise, the timing is from the last timing record to the current interval (in seconds).

{% hint style="danger" %}
Note that the name of the timed record must be unique within the current Stopwatch instance, ie it cannot be repeated. Otherwise, a LapNameError exception will be thrown.
{% endhint %}

Next, we use the `time.sleep` function again to suspend the current thread for 1 second, simulating the second phase of the program code to run.

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

Then, record the timing results of the second phase.

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

### Stop Stopwatch

In the above, we used the `time.sleep` function to simulate the first and second phases of the program run, and used the `lap` method to record the timing of the first and second phases of the program.

Now let's stop the `demo_stopwatch`.

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

Method `stop` returns a return value of data type `float` indicating the total time (in seconds) of the current Stopwatch instance from the first time it started to the time it was stopped.

The return value can be ignored, we can use the `get_watch` method to instantly check the total time of the Stopwatch instance:

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

The method `get_watch` has an optional parameter of data type `int` that indicates the total precision (decimal point) obtained. If the parameter is not supplied or the value is `None`, the default value for this parameter is the value of the `Stopwatch` class constructor method parameter `default_precision`.

{% hint style="danger" %}
Note that when using the get\_watch method in a Stopwatch version earlier than 0.1.3, you should ensure that the Stopwatch instance has stopped timing, otherwise a StatusError exception will be raised.
{% endhint %}

We can check the status of the current Stopwatch instance using the `get_status` method.

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

`StopwatchStatus` is a predefined Stopwatch state enumerator that can be compared to the return value of the method `get_status` .

```python
from stopwatch import StopwatchStatus
```

### Get Stopwatch record

In the above, we used the `lap` method to record the timing results of the first and second phases of the program in stages. Now we try to get these timing records.

#### Get named timing record

We can get the named timing record using the `get_lap` method.

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

The method `get_lap` has 2 parameters, which are:

| Parameter      | Type | Optional | Description                                                |
| -------------- | ---- | -------- | ---------------------------------------------------------- |
| lap\_name      | str  | False    | The unique name of the timed record.                       |
| lap\_precision | int  | True     | Get the timing record accuracy (number of decimal places). |

The parameter `lap_precision` is optional. If the parameter is not supplied or the value is `None`, the default value for this parameter is the value of the `Stopwatch` class constructor method parameter `default_precision`.

{% hint style="danger" %}
Note that if the specified timing record name does not exist, a LapNameError exception will be thrown.
{% endhint %}

If we need to determine if a timing record exists, we can check it with the `has_lap` method.

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

#### Get anonymous timing records

It's worth noting that anonymous timed records are not really anonymous, but instead use `lap_` and record number combination to name the timed record (for example: `lap_1`). Therefore, we can also get the anonymous timing record using the `get_lap` method, but the more convenient way is to use the `get_lap_by_number` method.

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

The difference from the method `get_lap` is that the method's parameter `lap_number` indicates the number of the anonymous record (starting at 1) and its data type is `int`. The `get_lap` method is still called inside the method to get the anonymous timing record.

{% hint style="danger" %}
Note that if you use this method to get a non-anonymous timing record, a LapNameError exception will be thrown.
{% endhint %}

To give you a clearer understanding of the relationship between named timing records and anonymous timing records, we will explain them in detail through the demo below.

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

In the sample code above, we recorded 2 current Stopwatch current timing results, one for each of the timed record and the anonymous timed record. The contents of the Timed Recordset in the Stopwatch instance are now as follows:

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

In the sample code above, an exception is thrown when the first named timing record is obtained using the `get_lap_by_number` method:

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

Because there is no named timing record named `lap_1` in the current record set of the Stopwatch instance, a `LapNameError` exception is inevitable. It can be seen that the anonymous timing record only names the record in the default form (a combination of `lap_` and record number).

#### Get the average result of all timing records

If we need to get the average timing of all the timed records in the current Stopwatch instance, we can use the `get_average_of_laps` method.

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

The method `get_average_of_laps` has an optional parameter `average_precision` of data type `int`, which indicates the precision (number of decimal places) of the average result of the acquired timed records. If the parameter is not supplied or the value is `None`, the method behaves the same as `get_lap`.

#### Enumerate all timing record names

We can also use the `get_laps` method to enumerate the unique names of all timed records, which also contain anonymous timed records.

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

The method `get_laps` returns the return value of the data type `list`, which contains the unique names of all the timed records, and the member data type is `str`.

#### Get the number of timing records

We can use the `get_lap_count` method to get the total number of timed records for the current Stopwatch instance.

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

### Reset Stopwatch

Each Stopwatch instance will generate status data for the current instance (for example: timing records, etc...) from the start of the count. If we need to clear this data, we should reset the Stopwatch instance to its initial state.

> **Tips**: The Stopwatch instance can repeat the start and end, and the timed record and total time will be accumulated until it is reset.

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

When the Stopwatch instance is reset, all state data is cleaned up and the Stopwatch instance is reset to its initial state.

{% hint style="danger" %}
Note that if the Stopwatch instance is still in a timed state, a StatusError exception will be raised. You can check the status of a Stopwatch instance using the get\_status method.
{% endhint %}

## Example

The following is a full Python sample code that demonstrates how to use Stopwatch.

### Source code

{% 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 %}

### Result

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