Что такое декоратор и как его написать?
- Как работают декораторы в Python?
- Напишите декоратор, который замеряет время выполнения функции
- Зачем нужен functools.wraps?
Декоратор — функция, которая принимает функцию и возвращает новую; @dec — сахар для f = dec(f).
- Обёртка внутри — замыкание, вызывает исходную через
*args, **kwargs functools.wrapsсохраняет имя и docstring исходной функции
А ещё Покажите декоратор с параметрами — это три уровня вложенности
Подробно
Функции в Python — объекты: их можно передавать и возвращать. Декоратор этим пользуется: получает функцию, определяет внутри обёртку, которая что-то делает до и после вызова, и возвращает обёртку.
Запись
@timer
def work(): ...
полностью эквивалентна work = timer(work) и выполняется один раз — при определении функции.
Обёртка — замыкание: она помнит исходную функцию из внешней области. Чтобы подходить к любой функции, обёртка принимает *args, **kwargs и передаёт их дальше, а результат возвращает.
Без @functools.wraps(func) у обёртки будет имя wrapper и пустой docstring — это ломает логи, отладку и документацию.
Декоратор с параметрами (@retry(3)) — это функция, которая возвращает декоратор: снаружи параметры, внутри декоратор, ещё глубже обёртка.
Примеры из жизни: @property, @staticmethod, @functools.lru_cache, маршруты во Flask и FastAPI (@app.get("/")), проверка прав, логирование, повторные попытки.
Пример
import functools
import time
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
try:
return func(*args, **kwargs)
finally:
print(f"{func.__name__}: {time.perf_counter() - start:.3f} с")
return wrapper
def retry(times):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(times):
try:
return func(*args, **kwargs)
except Exception:
if attempt == times - 1:
raise
return wrapper
return decorator
@timer
@retry(3)
def fetch():
...Что хотят услышать
- Определение через «функция принимает и возвращает функцию».
- Эквивалентность
@decиf = dec(f), момент применения. - Рабочий код обёртки с
*args, **kwargsи возвратом результата. functools.wrapsи декоратор с параметрами.
Частые ошибки
- Забыть
returnрезультата исходной функции в обёртке. - Вызвать функцию при декорировании:
return wrapper()вместоreturn wrapper. - Не знать, что порядок нескольких декораторов — снизу вверх.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Декоратор вызывается при каждом вызове функции, а не при её определении
- ✓Функция, принимающая функцию и возвращающая новую; @dec — сахар для f = dec(f)
- ✗Декоратор меняет байткод исходной функции
- ✗Декоратор нельзя применить к классу, только к функции
Что спросят дальше
- Что такое замыкание? Junior
- Что такое *args и **kwargs? Junior
Разбор темы
- Функции — аргументы, области видимости, замыкания и декораторы
Как передаются аргументы, *args и **kwargs, LEGB, ловушка изменяемого значения по умолчанию, замыкания и позднее связывание, декораторы и functools.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.