TGViewer
Библиотека питониста | Python, Django, Flask Библиотека питониста | Python, Django, Flask @pyproglib · 37.3K subscribers
Post #7430 3.55K
👇 Как правильно типизировать декораторы в Python

Декораторы — штука мощная, но для типизации долгое время были настоящим кошмаром. Кажется логичным сказать: «декоратор принимает функцию и возвращает такую же функцию». На практике mypy отвечает ошибками, потому что wrapper теряет исходную сигнатуру.

Вот как обычно пытаются сделать (и почему это ломается):

from functools import wraps
from typing import Any, Callable, TypeVar

F = TypeVar("F", bound=Callable[..., Any])

def info(func: F) -> F:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
print(func.__name__)
return func(*args, **kwargs)

return wrapper # mypy: incompatible return type


Типизатор видит, что wrapper — это уже другая функция с Callable[..., Any], а не тот же F.

Правильное современное решение — ParamSpec, который сохраняет параметры исходной функции:

from functools import wraps
from typing import Callable, ParamSpec, TypeVar

P = ParamSpec("P")
R = TypeVar("R")

def info(func: Callable[P, R]) -> Callable[P, R]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(func.__name__)
print(func.__doc__)
return func(*args, **kwargs)

return wrapper


Теперь сигнатура полностью «протаскивается» через декоратор и mypy счастлив.

А в Python 3.12+ это стало ещё красивее благодаря PEP 695:

from functools import wraps
from typing import Callable

def info[**P, R](func: Callable[P, R]) -> Callable[P, R]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(func.__name__)
print(func.__doc__)
return func(*args, **kwargs)

return wrapper


Без лишних объявлений, всё читается почти как generics в статически типизированных языках.

➡️ Подробная статья

📍 Навигация: Вакансии • Задачи • Собесы

🐸 Библиотека питониста

#буст
  • ❤ 9
  • 👍 5
  • 🌚 1
More from @pyproglib
  1. Oct 3, 2026📚Напоминаем про наш полный курс «Самоучитель по Python для начинающих» Мы написали и собр…
  2. Oct 2, 2026🎹 Что делать после ChatGPT? Следующий шаг — AI-агенты. Они не просто отвечают на запросы,…
  3. Sep 30, 2026🔥 Последний день для разработчиков AI уже пишет код. Теперь важнее научиться встраивать е…
  4. Sep 25, 2026🔥 Курс «ИИ для разработчиков» уже стартовал И мы решили открыть первое занятие всем — мож…
  5. Sep 20, 2026👻 94% токенов уходят не на код Анализ запусков показал: основная часть токенов уходит на…
  6. Sep 19, 2026📚Напоминаем про наш полный курс «Самоучитель по Python для начинающих» Мы написали и собр…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →