49  Context Manager (Custom)

import time

49.1 @contextmanager Decorator

49.1.1 file_manager()

import time
from contextlib import contextmanager
from typing import Generator, IO, Any
@contextmanager
def file_manager(path: str, mode: str) -> Generator[IO, Any, None]:
    file: IO = open(path, mode)
    print('Opening file...')
    try:
        yield file
    finally:
        print('Closing file...')
        if file:
            file.close()
with file_manager("hello.txt", mode="r") as f:
    raise Exception("something bad happens!")
    print(f.read())
Opening file...
Closing file...
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Cell In[6], line 2
      1 with file_manager("hello.txt", mode="r") as f:
----> 2     raise Exception("something bad happens!")
      3     print(f.read())

Exception: something bad happens!

49.1.2 timer() ⭐️

import time
from contextlib import contextmanager
from typing import Generator, IO, Any
@contextmanager
def timer():
    """Context manager for measuring execution time.
    
    Returns
    -------
    callable
        A lambda function that returns the elapsed time
        
    Example
    -------
    ```python
    with timer() as t:
        # some code to measure
        pass
    t = t() 
    t  # prints elapsed time
    ```
    """
    start_time: float = time.perf_counter()
    try:
        yield lambda: elapsed
    finally:
        elapsed = time.perf_counter() - start_time
        print(f'Time: {elapsed:.4f}s')
with timer() as t:
    text = ""
    for i in range(100_000):
        text += str(i)
Time: 0.3740s
t()
0.3740487500326708

49.1.3 timer() start & end

from datetime import datetime

@contextmanager
def timer2():
    start_time: float = time.perf_counter()
    print(f"Started at: {datetime.now():%H:%M:%S}")
    try:
        yield lambda: elapsed
    finally:
        elapsed = time.perf_counter() - start_time
        print(f"Ended at: {datetime.now():%H:%M:%S}")
        print(f'Time: {elapsed:.4f}s')
with timer2() as t2:
    text = ""
    for i in range(100_000):
        text += str(i)
Started at: 16:36:24
Ended at: 16:36:24
Time: 0.3461s

49.2 Timer Class

class Timer:
    def __init__(self, name: str):
        self.name = name
    def __enter__(self):
        self._start_time = time.perf_counter()
        print(f"Starting timer for {self.name}")
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        self._end_time = time.perf_counter()
        elapsed_time = self._end_time - self._start_time
        print(f"Timer for {self.name} finished. Elapsed time: {elapsed_time:.4f} seconds")
with Timer("for...loop timer") as timer:
    print(timer.name)
    for _ in range(1000000):
        pass
Starting timer for for...loop timer
for...loop timer
Timer for for...loop timer finished. Elapsed time: 0.0449 seconds