""" Урок 64. Декораторы — весь код примеров одним файлом. Источник: subjects/python-fundamentals/course/lessons/64-decorators/examples.html Файл собран автоматически (tools/build_lesson_examples.py): правьте страницу урока. Запуск: python lesson-64.py """ # ==================================================================== # Пример 1. Логирование вызовов # ==================================================================== import logging import functools logging.basicConfig( filename="functions.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", encoding="utf-8", ) def log_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): logging.info(f"{func.__name__} called with {args}, {kwargs}") result = func(*args, **kwargs) logging.info(f"{func.__name__} returned {result}") return result return wrapper @log_decorator def multiply(a: int, b: int) -> int: return a * b print(multiply(4, 5)) # ==================================================================== # Пример 2. Измерение времени выполнения # ==================================================================== import time import functools def timing_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) elapsed = time.perf_counter() - start print(f"{func.__name__} took {elapsed:.5f} seconds") return result return wrapper @timing_decorator def slow_function() -> None: time.sleep(0.1) print("Done") slow_function() # ==================================================================== # Пример 3. Автоматический retry # ==================================================================== import time import functools def retry(attempts: int, delay: float = 1.0): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): for attempt in range(1, attempts + 1): try: return func(*args, **kwargs) except Exception as exc: print(f"Attempt {attempt} failed: {exc}") if attempt < attempts: time.sleep(delay) print("All attempts exhausted.") return None return wrapper return decorator @retry(3, delay=0.5) def read_file(filename: str) -> str | None: with open(filename, "r", encoding="utf-8") as file: return file.read() print(read_file("data.txt")) # ==================================================================== # Пример 4. Рамка вокруг вывода # ==================================================================== import functools def framed(func): @functools.wraps(func) def wrapper(*args, **kwargs): border = "=" * 40 print(border) result = func(*args, **kwargs) print(border) return result return wrapper @framed def show_title() -> None: print("== Menu ==") show_title() # ==================================================================== # Пример 5. Настраиваемая рамка # ==================================================================== import functools def framed(width: int, symbol: str = "="): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): border = symbol * width print(border) result = func(*args, **kwargs) print(border) return result return wrapper return decorator @framed(30, "-") def show_title() -> None: print("== Menu ==") show_title() # ==================================================================== # Пример 6. Среднее время за несколько вызовов # ==================================================================== import time import functools def measure_time(repeats: int = 5): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): total = 0.0 for _ in range(repeats): start = time.perf_counter() result = func(*args, **kwargs) total += time.perf_counter() - start average = total / repeats print(f"Average execution time for {repeats} calls: {average:.2f} seconds") return result return wrapper return decorator @measure_time(5) def compute() -> int: total = 0 for i in range(1_000_000): total += i return total compute()