Представьте, что вам захотелось иметь возможность выводить на печать все результаты вызова некой функции. Однако саму функцию модифицировать не хочется, а вместо этого вы желаете получить универсальный инструмент, пригодный для использования с любыми функциями.
Давайте реализуем такую функцию, для чего воспользуемся ФВП (функцией высшего порядка):
def printing(function):
def inner(*args, **kwargs):
result = function(*args, **kwargs)
print('result =', result)
return result
return inner
def add_one(x):
return x + 1
add_one = printing(add_one)
y = add_one(10)
# => result = 11
y
# 11
Сначала разберёмся с функцией printing
. Эта функция создаёт замыкание inner
, которое принимает любые аргументы, применяет к ним функцию, печатает результат и тут же возвращает его. Заметьте, в определении inner
я использовал аргументы *args, **kwargs
, которые функция без изменения передаёт замкнутой функции function
— именно так в Python объявляют "всеядные" функции, которые "пробрасывают" любые комбинации аргументов.
Теперь посмотрим на пример применения printing
. В примере я заменил функцию обёрнутым (wrapped) вариантом через присваивание старому имени функции нового значения. Да, функции в Python — обычные переменные в той области видимости, где были объявлены!
Имя "printing" ("печатающий") я выбрал неспроста — подобные обёртки над функциями часто называют в похожей манере. Ведь и по смыслу поведение, которое добавляет обёртка к исходной функции, служит дополнением для этой функции. И читается printing(add_one)
практически по-человечески: "добавить единицу, печатая на экран (результат)".
Подобное оборачивание — довольно частая операция в коде, широко использующем функции высшего порядка. Авторы языка Python даже завели для удобного использования функций-обёрток специальный синтаксис! Вот эти самые функции-обёртки вместе с синтаксисом оборачивания называются декораторами.
Тут я вынужден предупредить, что те из вас, кто знаком с паттерном проектирования "Декоратор", могут смело забыть всё, что они знали о нём! Дело в том, что декораторы в Python — сугубо самостоятельная вещь, а не реализации упомянутого паттерна. Те, кому словосочетание "паттерн Декоратор" ничего не говорит, могут не обращать на это замечание внимания :)
Применения декоратора printing
к функции add_one
с использованием специального синтаксиса можно записать так:
@printing
def add_one(x):
return x + 1
Имя декоратора пишется на строчке, предшествующей заголовку функции, а перед именем пишется символ @
. После такого применения декоратора нам уже не нужно "переприсваивать" функцию (add_one = printing(add_one)
)!
Применение даже одного декоратора становится удобнее с таким синтаксисом, но применять подобным образом можно сколько угодно декораторов! Выглядеть это будет так:
@logging
@printing
@cached
def foo():
# …
Что будет равнозначно коду
foo = cached(foo)
foo = printing(foo)
foo = logging(foo)
# или в одну строку
foo = logging(printing(cached(foo)))
Обратите внимание и запомните: оборачивание происходит сначала в ближайшие к имени функции обёртки, как бы "изнутри наружу" —
cached
, затемprinting
и в концеlogging
.
Вам ответят команда поддержки Хекслета или другие студенты.
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт