Как написать декоратор для измерения времени выполнения?python-54

Базовый декоратор времени выполнения

Самый простой вариант декоратора, который измеряет время выполнения функции:

import time
from functools import wraps

def timing_decorator(func):
    # Используем wraps для сохранения метаданных функции
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()  # Начальное время

        result = func(*args, **kwargs)   # Вызов оригинальной функции

        end_time = time.perf_counter()   # Конечное время
        elapsed_time = end_time - start_time

        print(f"Функция {func.__name__} выполнилась за {elapsed_time:.4f} секунд")
        return result

    return wrapper

Использование:

@timing_decorator
def calculate_sum(n):
    return sum(range(n))

result = calculate_sum(1_000_000)

Вывод:

Функция calculate_sum выполнилась за 0.0342 секунд

Улучшенная версия с настройкой единиц измерения

def timing_decorator(units='seconds'):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.perf_counter()
            result = func(*args, **kwargs)
            end_time = time.perf_counter()

            elapsed = end_time - start_time
            if units == 'milliseconds':
                elapsed *= 1000
                unit_name = 'миллисекунд'
            elif units == 'microseconds':
                elapsed *= 1_000_000
                unit_name = 'микросекунд'
            else:
                unit_name = 'секунд'

            print(f"{func.__name__} - время выполнения: {elapsed:.2f} {unit_name}")
            return result
        return wrapper
    return decorator

Использование с параметрами:

@timing_decorator(units='milliseconds')
def process_data(data_size):
    return [x**2 for x in range(data_size)]

process_data(100_000)

Декоратор с возвратом времени выполнения

Для случаев, когда нужно использовать время выполнения в коде:

def return_timing(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        return result, end - start
    return wrapper

Использование:

@return_timing
def complex_calculation(n):
    return sum(x*x for x in range(n))

result, exec_time = complex_calculation(1_000_000)
print(f"Вычисления заняли {exec_time:.5f} секунд")

Декоратор для методов класса

def method_timing_decorator(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        start = time.perf_counter()
        result = func(self, *args, **kwargs)
        elapsed = time.perf_counter() - start

        # Используем имя класса и метода
        print(f"{self.__class__.__name__}.{func.__name__} - {elapsed:.4f} сек")
        return result
    return wrapper

Пример использования в классе:

class DataProcessor:
    @method_timing_decorator
    def process(self, data):
        time.sleep(0.5)  # Имитация долгой обработки
        return [x.upper() for x in data]

processor = DataProcessor()
processor.process(['a', 'b', 'c'])

Продвинутая версия с логгированием

import logging
from datetime import datetime

def logged_timing(logger=None):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.perf_counter()
            result = func(*args, **kwargs)
            elapsed = time.perf_counter() - start_time

            log_msg = (
                f"TIMING | {datetime.now().isoformat()} | "
                f"{func.__module__}.{func.__name__} | "
                f"{elapsed:.6f} сек"
            )

            if logger:
                logger.info(log_msg)
            else:
                print(log_msg)

            return result
        return wrapper
    return decorator

Настройка и использование:

# Создаем логгер
logging.basicConfig(level=logging.INFO)
app_logger = logging.getLogger('app_performance')

@logged_timing(logger=app_logger)
def database_query():
    time.sleep(0.1)
    return "результат запроса"

database_query()

Резюмируем

декораторы для измерения времени выполнения - это мощный инструмент для профилирования кода. Они позволяют легко добавлять временные метки к любым функциям без изменения их основного кода. Вы можете адаптировать приведенные примеры под свои нужды - добавлять логирование, сохранять результаты таймингов или настраивать формат вывода.