Контекст
Мобильный SDK продуктовой аналитики оброс трекерами: одна функция шлёт события экранов, другая — клики, третья — покупки. Команде роста нужно знать, сколько раз приложение дёрнуло каждый трекер за сессию, но вписывать counter += 1 в каждую функцию — значит размазать учёт по всему коду. Декоратор решает это одним движением: счётчик живёт в обёртке, а сами трекеры не меняются ни на строку.
Задача
Реализуйте декоратор count_calls(fn): он принимает функцию и возвращает обёртку, которая ведёт счётчик собственных вызовов в атрибуте calls.
Правила
- Сразу после декорирования
wrapper.calls == 0— ещё до первого вызова. - Каждый вызов обёртки увеличивает
callsна 1 и вызываетfnсо всеми переданными аргументами — и позиционными, и именованными (*args, **kwargs). - Результат
fnвозвращается без изменений. - Счётчики разных задекорированных функций независимы: у каждой — своя обёртка и свой
calls. - Имя обёртки не сохраняем — осознанное упрощение.
Примеры
@count_calls
def track_event(name, value=1):
return f"{name}:{value}"
track_event.calls
# 0
track_event("открытие_экрана")
# "открытие_экрана:1"
track_event("клик_по_кнопке", value=3)
# "клик_по_кнопке:3"
track_event.calls
# 2
Где это пригодится
Счётчик вызовов — простейшая метрика, с которой начинается любой мониторинг: сколько раз дёрнули ручку API, сколько событий ушло в очередь. А приём «состояние в атрибуте обёртки» — основа более серьёзных декораторов: кэшей, ограничителей частоты, сборщиков статистики.