
Декоратор в Python – это функция, которая принимает другую функцию в качестве аргумента и возвращает модифицированную версию этой функции. Такой подход позволяет добавлять логику, не изменяя исходный код. Например, с помощью декоратора можно измерять время выполнения функции, автоматически логировать вызовы или проверять права доступа.
Механизм основан на концепции функций как объектов первого класса: их можно передавать в переменные, использовать как аргументы и возвращать из других функций. Именно это свойство делает возможным применение оператора @, который связывает декоратор с целевой функцией.
Чтобы разобраться в создании декоратора, важно поэтапно изучить три шага: сначала определить простую функцию-обёртку, затем научиться возвращать её из другой функции, и только после этого использовать синтаксический сахар @decorator. Такой порядок избавляет от путаницы и позволяет увидеть, как именно работает подмена функции.
В процессе обучения стоит уделить внимание модулю functools, особенно функции wraps, которая сохраняет имя и документацию исходной функции. Без неё отладка и автодокументация становятся затруднительными, поскольку Python будет отображать данные об обёртке, а не о реальной функции.
Определение функции внутри функции

Функция, объявленная внутри другой функции, называется вложенной. Она доступна только в области видимости внешней функции и не существует за её пределами.
В контексте декораторов вложенные функции используются для обёртывания вызова исходной функции дополнительной логикой. Например, можно выполнить проверку аргументов или зафиксировать время выполнения.
Ключевой момент: внутренняя функция должна возвращаться из внешней. Без этого механизм подмены исходной функции в декораторе не сработает.
Пример:
def wrapper(func):
def inner(*args, **kwargs):
print("Вызов функции:", func.__name__)
return func(*args, kwargs)
return inner
В данном примере inner создаётся при вызове wrapper и возвращается как результат. Таким образом обеспечивается возможность вставить произвольную логику до и после вызова исходной функции.
Передача функции как аргумента
Пример базовой передачи:
def printer(func):
print("Вызов:", func.__name__)
return func
def hello():
return "Привет!"
result = printer(hello)
print(result()) # Привет!
Здесь функция hello передана в printer без вызова (без скобок). Аргументом выступает сам объект функции.
- При передаче всегда указывайте имя функции без скобок, иначе будет передан результат её выполнения.
- Можно передавать как встроенные функции (
len,sorted), так и пользовательские. - Для сохранения метаданных используйте модуль
functoolsи декоратор@wraps.
Передача функций как аргументов особенно полезна для:
- Логирования действий – переданная функция вызывается внутри обёртки с добавлением сообщений.
- Измерения времени выполнения – функция запускается в обёртке с таймингом.
- Контроля доступа – вызов происходит только при выполнении условий.
Возврат функции из функции
В Python функция может возвращать другую функцию. Это позволяет создавать динамическое поведение и формировать гибкие конструкции для будущего вызова.
Простейший пример:
def outer():
def inner():
return "Результат из inner"
return inner
f = outer()
print(f()) # Результат из inner
Ключевые моменты:
- Вместо вычисленного значения возвращается объект функции.
- Функция, полученная в результате вызова, сохраняет доступ к окружению, в котором была создана.
- Такой механизм используется для написания декораторов и фабрик функций.
Практическое применение:
- Инкапсуляция логики – скрытые функции позволяют вынести вспомогательные вычисления.
- Передача параметров – замыкания фиксируют значения переменных без явной передачи аргументов.
- Гибкие интерфейсы – можно создавать функции-конфигураторы, которые возвращают разные варианты поведения.
Пример использования замыкания:
def power(exp):
def calc(base):
return base exp
return calc
square = power(2)
cube = power(3)
print(square(5)) # 25
print(cube(5)) # 125
Возврат функции обеспечивает основу для построения декораторов: внешняя функция формирует внутреннюю, которая расширяет или изменяет поведение исходного кода.
Добавление функционала перед и после вызова

Декоратор позволяет внедрять дополнительное поведение до и после выполнения функции. Это удобно для логирования, проверки аргументов, измерения времени выполнения или автоматической обработки результатов.
Базовый пример:
def log_wrapper(func):
def wrapper(*args, **kwargs):
print("Старт функции:", func.__name__)
result = func(*args, **kwargs)
print("Завершение функции:", func.__name__)
return result
return wrapper
@log_wrapper
def calculate(x, y):
return x + y
calculate(3, 7)
Рекомендации по внедрению дополнительного кода:
| Этап | Цель | Пример действий |
|---|---|---|
| До вызова | Подготовка окружения, контроль входных данных | Проверка типов, запись в журнал, старт таймера |
| После вызова | Анализ результата и освобождение ресурсов | Подсчет времени, форматирование ответа, закрытие соединений |
При расширении функционала важно, чтобы декоратор возвращал исходный результат, иначе изменится поведение программы.
Использование синтаксиса @ для применения декоратора

Символ @ размещается непосредственно перед определением функции и связывает её с декоратором. Такой способ полностью эквивалентен явному вызову вида func = decorator(func), но делает код компактнее и читаемее.
Пример:
def logger(func):
def wrapper(*args, **kwargs):
print(f"Вызов {func.__name__} с аргументами {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@logger
def calculate(a, b):
return a + b
При вызове calculate(3, 4) сначала выполнится логика внутри wrapper, затем – исходная функция. Без использования @ пришлось бы писать calculate = logger(calculate), что менее наглядно.
Синтаксис @ поддерживает цепочку декораторов: они применяются сверху вниз. Например:
@decorator_one
@decorator_two
def process():
pass
Здесь сначала сработает decorator_two, а затем результат его работы будет передан в decorator_one. Такое поведение важно учитывать при проектировании.
Передача аргументов в декоратор

Декораторы в Python могут принимать собственные аргументы, что повышает их гибкость. Для этого требуется создать три уровня функций: внешний уровень принимает аргументы декоратора, средний – функцию, которую декорируют, и внутренний – выполняет обёртку.
Пример передачи аргументов в декоратор:
Пример:
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
Использование:
@repeat(times=3)
def greet(name):
print(f»Hello, {name}!»)
Важный момент: внутренний wrapper должен принимать *args и **kwargs, чтобы сохранялась совместимость с функциями любой сигнатуры. Аргументы внешнего уровня становятся доступными только внутри обёртки через замыкание.
Для повышения читаемости рекомендуется именовать функции на каждом уровне осмысленно, например repeat_decorator и repeat_wrapper, особенно при сложных декораторах с несколькими аргументами.
Если декоратор должен возвращать значение, внутренний wrapper обязательно использует return для результата декорируемой функции, иначе исходное значение теряется.
Вопрос-ответ:
Что такое декоратор в Python и зачем он нужен?
Декоратор — это функция, которая принимает другую функцию и возвращает новую функцию с добавленным поведением. С помощью декораторов можно, например, добавлять логирование, проверку прав доступа или измерение времени выполнения без изменения исходного кода основной функции.
Как правильно создать простой декоратор с аргументами функции?
Для создания декоратора, который работает с функциями, принимающими аргументы, используют конструкцию с внутренней функцией и *args, **kwargs. Внешняя функция принимает функцию для обёртки, внутренняя — те же аргументы, что и исходная функция. Внутри внутренней функции можно выполнить дополнительные действия до и после вызова оригинальной функции, а затем вернуть результат.
Можно ли использовать один декоратор для нескольких функций?
Да, один и тот же декоратор можно применять к разным функциям. Для этого его объявляют отдельно и просто добавляют к каждой функции с помощью синтаксиса @имя_декоратора. Каждый вызов функции с декоратором создаёт отдельную обёртку, но логика добавленного поведения остаётся одинаковой.
Как сохранить информацию о исходной функции после применения декоратора?
При использовании декоратора Python заменяет исходную функцию на обёртку, из-за чего теряются её имя, документация и аннотации. Чтобы сохранить эти данные, применяют functools.wraps. Это декоратор для декоратора, который копирует атрибуты исходной функции в обёртку, позволяя работать с ней так, как с оригиналом.
Какие ошибки чаще всего встречаются при написании декораторов?
Часто совершаемые ошибки включают игнорирование передачи аргументов через *args и **kwargs, что вызывает ошибки при вызове функций с параметрами; забывание вернуть результат исходной функции; а также потерю информации о функции без использования functools.wraps. Проверка каждого шага и тестирование с разными типами функций помогает избежать таких проблем.
Что такое декоратор в Python и для чего его применяют?
Декоратор — это функция, которая изменяет или расширяет поведение другой функции без изменения её исходного кода. Обычно его используют, чтобы добавлять повторяющийся функционал, например, логирование вызовов, проверку прав доступа или измерение времени выполнения. Декоратор принимает на вход функцию, оборачивает её и возвращает новую функцию с дополнительной логикой. Это позволяет писать более чистый и удобный код, избегая дублирования одних и тех же действий в разных местах программы.
Как создать собственный декоратор шаг за шагом и применить его к функции?
Создание декоратора состоит из нескольких этапов. Сначала определяется функция-декоратор, которая принимает другую функцию в качестве аргумента. Внутри создаётся вложенная функция, которая выполняет нужные действия до или после вызова исходной функции, затем возвращает результат её работы. После этого внешняя функция возвращает вложенную. Чтобы применить декоратор, перед определением функции ставят символ @ и имя декоратора. Например, если нужен декоратор для измерения времени выполнения функции, внутри вложенной функции можно зафиксировать момент начала и конца выполнения, вычислить разницу и вывести её на экран. Такой подход помогает добавить дополнительное поведение к функции без изменения её кода.
