Базовый декоратор
Декоратор принимает функцию, добавляет поведение и возвращает новую функцию — обёртку. Внутри используется вложенная функция и замыкание.
def simple_decorator(func):
def wrapper(*args, **kwargs):
print("Before call")
result = func(*args, **kwargs)
print("After call")
return result
return wrapper
@simple_decorator
def greet(name: str) -> str:
return f"Hello, {name}!"
print(greet("Alice"))
Параметризованный декоратор
Если декоратор должен принимать собственные аргументы, нужна тройная вложенность: внешняя функция получает параметры, средняя — декорируемую функцию, внутренняя — вызывает её.
def repeat(times: int):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def say_hi():
print("Hi")
say_hi()
Состояние в замыкании
Декоратор может хранить данные между вызовами: кэш результатов, счётчики обращений, журналы. Изменяемые объекты (словари, множества, списки) удобно держать в замыкании внешней функции.
def call_counter(func):
counts: dict[str, int] = {}
def wrapper(*args, **kwargs):
name = func.__name__
counts[name] = counts.get(name, 0) + 1
print(f"{name} called {counts[name]} times")
return func(*args, **kwargs)
return wrapper
Логирование через logging
Модуль logging предоставляет гибкую систему записи событий. Базовую настройку задают через logging.basicConfig: имя файла, уровень, формат сообщения, кодировка.
import logging
logging.basicConfig(
filename="app.log",
level=logging.INFO,
format="%(message)s",
encoding="utf-8",
)
Проверка типов аргументов
Функция isinstance(obj, type_) проверяет, принадлежит ли объект указанному типу. В отличие от сравнения type(obj) is type_, она учитывает наследование.
def accepts_int(func):
def wrapper(value):
if not isinstance(value, int):
raise TypeError(f"Expected int, got {type(value).__name__}")
return func(value)
return wrapper
Современные замечания
- PEP 8: имена функций и переменных —
snake_case, имена декораторов тоже. - f-strings: предпочтительнее конкатенации и
str.formatдля читаемости. - Аннотации типов: помогают IDE и статическим анализаторам, но не влияют на выполнение.
- Guard clauses: ранняя проверка и выход из функции упрощает вложенность.
- functools.wraps: сохраняет
__name__,__doc__и другие атрибуты исходной функции.