Декораторы Python на собеседовании
Написать декоратор просят редко, а объяснить, когда он выполняется и что делает с именем функции, — почти всегда. Разбираем, что проверяет этот вопрос.
Вопрос про декораторы почти никогда не бывает одиночным. Разработчик Авито, который сам проводит собеседования, опубликовал свою лесенку по этой теме — в ней тринадцать шагов: от «что такое декоратор» через «примените его без синтаксического сахара» и «а теперь с аргументом» до func.__doc__ и порядка выполнения при импорте. Ниже — четыре узла, на которые эта лесенка опирается; зная их, можно вывести остальное, а не вспоминать.
Всё, что сказано о поведении, относится к Python 3.
Декоратор — это присваивание
Отправная точка задана документацией без всяких оговорок: синтаксис декоратора — это просто синтаксический сахар. Словарь языка приводит рядом два определения и называет их семантически эквивалентными: функция, определённая обычным образом и следом переприсвоенная как f = staticmethod(f), — и та же функция под @staticmethod. Разницы между ними нет никакой.
Отсюда рабочее определение, из которого дальше выводится всё: декоратор — вызываемый объект, который получает функцию единственным аргументом, а его результат связывается с именем функции вместо неё самой. Именно так это сформулировано в справочнике языка, и в такой формулировке уже спрятаны ответы на половину лесенки.
Полезно проговорить следствие, которое обычно проскакивают: никакой магии в @ нет, и любой декоратор можно применить руками — записав то же присваивание. Способность сделать это на бумаге и есть содержание шага «примените без сахара».
Когда декоратор выполняется
Здесь заученный ответ обычно и ломается. Справочник языка формулирует правило прямо: выражения декораторов вычисляются в момент определения функции, в области, содержащей это определение. Не при вызове функции. Не при первом обращении. При определении.
Практическое следствие проверяется одним экспериментом, и именно он стоит в лесенке предпоследним: если декоратор печатает что-нибудь до того, как вернуть обёртку, этот вывод появится при импорте модуля — когда декорируемую функцию ещё никто не вызывал. Модуль импортирован, ни одна функция не вызвана, а строки уже напечатаны.
Из этого же правила выводится ответ на вопрос про параметризованный декоратор, до которого мы дойдём в следующей секции: @decorator(10) означает, что decorator(10) выполняется прямо при определении — и только его результат становится декоратором.
Стоит отметить и вторую половину правила, про которую забывают: вычисление происходит в области, содержащей определение. Декоратор видит имена того места, где стоит @, а не того, где потом вызовут функцию.
Почему у параметризованного декоратора три уровня
Свидетельство описывает это место как то, где ломается часть кандидатов, прошедших предыдущие шаги, и объясняет причину так: общий вид декоратора заучивают, не понимая механики. Проверить, понята ли механика, легко — правилом из документации.
Справочник даёт преобразование дословно: функция под двумя декораторами — @f1(arg) сверху и @f2 снизу — примерно эквивалентна тому же определению плюс одна строка присваивания: func = f1(arg)(f2(func)).
Из этой строки читается сразу всё, что спрашивают. Порядок применения — изнутри наружу: сначала f2, ближайший к функции, потом результат отдаётся наружу. Три уровня вложенности параметризованного декоратора — не традиция, а прямое следствие: f1(arg) — это вызов, который должен вернуть декоратор; тот принимает функцию и возвращает обёртку. Отсюда и три уровня: функция от аргументов → функция от функции → обёртка.
Документация добавляет оговорку, которую стоит воспроизводить вместе с правилом: приведённая запись эквивалентна не полностью — исходная функция не связывается временно с именем func. То есть промежуточного состояния, в котором func указывает на недекорированную функцию, не существует.
Что происходит с именем функции
Этот шаг в лесенке заходит через func.__doc__, и он проверяет, дочитано ли до конца правило из первой секции. Возвращённое декоратором значение связывается с именем функции вместо объекта функции. Значит, после декорирования имя указывает уже не на исходную функцию, а на обёртку — со всеми её атрибутами.
Отсюда выводится, а не запоминается, весь набор последствий: __name__ покажет имя обёртки, __doc__ вернёт её докстроку — то есть, скорее всего, None, потому что докстроку внутренней обёртке обычно не пишут. Исходная функция никуда не делась, она захвачена замыканием, но по имени доступна уже не она.
Только теперь имеет смысл называть средство: functools.wraps — декоратор, который переносит эти атрибуты с исходной функции на обёртку. Полезно понимать, что он не меняет механики: имя по-прежнему связано с обёрткой, просто у обёртки теперь чужие метаданные. Это косметика поверх правила, а не отмена правила.
Практическая цена вопроса — не в красоте: на потерянных метаданных ломаются вещи, которые их читают. Инструменты документации, логи с именем функции, отладка — везде, где что-то смотрит на __name__, вместо осмысленного имени появится wrapper.
Последние шаги лесенки — декоратор для корутины и один декоратор, работающий и с обычной функцией, и с асинхронной, — это уже разговор про asyncio: обёртка вокруг корутины сама должна быть корутиной, иначе результат перестанет быть ожидаемым объектом. Механика декоратора при этом не меняется ни в чём — меняется то, что именно возвращает обёртка.

Backend-разработчик Python
Учебный маршрут backend-разработчика на Python: язык и среда выполнения CPython, конкурентность, веб-фреймворки и API, данные и ORM, безопасность, продакшн-инженерия и архитектура.

Backend Developer Python
Глубокое понимание Python и его среды выполнения для backend-разработки. Курс разбирает не синтаксис, а семантику языка и поведение CPython: объектную модель и работу со ссылками, модель данных и протоколы, конкурентность на asyncio и следствия глобальной блокировки интерпретатора, прикладной веб-слой (FastAPI, Django, Flask), работу с данными через ORM и продакшн-инженерию. Цель — научиться видеть стоимость абстракций и поведение кода под нагрузкой, а не просто заставлять его работать.
Готовьтесь не вслепую
Возьмите маршрут по своей роли и отрабатывайте ответы на вопросы с AI-разбором.