Может ли декоратор в Python не возвращать wrapper и что будет в этом случае
Что проверяют
Понимаете ли вы, что @decorator это просто присваивание func = decorator(func), а не магия с обязательной обёрткой. Следом обычно спрашивают, когда выполняется код декоратора и зачем нужен functools.wraps.
Как отвечать
Может. Python не требует от декоратора возвращать обёртку: имя функции связывается с тем, что декоратор вернул. Отсюда несколько случаев:
- вернул
None: имя указывает наNone, и первый же вызов даётTypeError: 'NoneType' object is not callable. Обычно это не замысел, а забытыйreturn wrapper; - вернул исходную функцию: законный паттерн регистрации. Декоратор кладёт функцию в реестр и отдаёт её без изменений, так работают
@app.routeво Flask,atexit.registerиregisterуfunctools.singledispatch; - вернул другой объект:
@propertyвозвращает дескриптор,@functools.lru_cacheвозвращает объект с методомcache_info(),@dataclassвозвращает класс с добавленными методами; - вернул что угодно ещё: хоть число. С Python 3.9 (PEP 614) после
@допустимо любое выражение, но такие трюки только запутывают читателя.
Обязательно проговорите время выполнения: тело декоратора выполняется один раз при определении функции, то есть при импорте модуля, а не при каждом вызове. print внутри декоратора сработает при import, даже если функцию никто не вызвал.
Если обёртка всё же нужна, её оформляют через @functools.wraps(func): так сохраняются __name__ и __doc__ и появляется __wrapped__, на который опираются inspect.signature и pytest.
Где спотыкаются
Говорят, что без обёртки ошибка случится на этапе декорирования. Декорирование проходит молча, падает только вызов, иногда сильно позже и в другом модуле.
Путают декоратор с параметрами: для retry(times) пишут @retry вместо @retry(3). Функция попадает в параметр times, а имя связывается с внутренним декоратором.
Возвращают экземпляр класса с __call__ и вешают его на метод. Без __get__ такой объект не дескриптор, self не подставляется, и вызов метода ломается на аргументах.