Что такое декоратор в Python и почему меня это должно волновать?

Первоначально опубликовано в блоге Coding Duck: ccstechme.com/coding-duck-blog

Всем привет и добро пожаловать в очередное обсуждение Py in 5. Серия статей, в которой я беру концепцию программирования на Python и пытаюсь объяснить ее человеческим языком за 5 минут чтения или меньше. Наша тема сегодня: Декораторы!

Как и наш клиент, давайте получим «официальное» определение декораторов:

pythontips.com:

Функции, изменяющие функциональность других функций

Python.org:

Функция, возвращающая другую функцию, примененную как преобразование функции с использованием синтаксиса @wrapper

Итак, то, что я слышу, это….функции. Функции, которые принимают функцию для возврата функции, чтобы ваше приложение функционировало функционально. И парадигма программирования обычно используется? Объектно-ориентированный… очевидно.

Несмотря на то, что эти определения вовсе не болтают по кругу и не используют свое собственное определение для самоопределения, я все равно попытаюсь прояснить ситуацию.

В самом абстрактном смысле думайте о декораторах как о своего рода инкапсуляции, но на уровне функций, а не на уровне классов. Подобно тому, как классы в Python позволяют инкапсулировать свои свойства, декораторы допускают такой тип ООП внутри функций. Это похоже на то, как вы импортируете класс в свой файл Python, скажем, модуль «время». Допустим, вы хотите сказать своей программе подождать 10 секунд, прежде чем продолжить. Для этого вы должны написать: time.sleep(10). Здесь вы в основном используете инкапсуляцию. Функция «сна» инкапсулирована (или заключена) в класс времени.

Думайте об этом, как если бы у вас был друг по имени Сон, с которым вы хотели поиграть в детстве. Вы приходили к нему домой, звонили в звонок, и его отец Тайм открывал дверь. "Привет! Сон может выйти и поиграть?» Вы спросите Время.

"Конечно!" он отвечал: «Позвольте мне найти его для вас». Затем выходил Sleep, и вы отправлялись веселиться в старые добрые времена. Декоратор предоставляет аналогичную функциональность, но инкапсулирует не функции, а контекст. Любая функция, которая вызывается с помощью декоратора, получает доступ к контексту (вещам, которые происходят до и после запуска функции, которые влияют на функцию во время ее выполнения), который уникален для этого декоратора. Вроде как у ваших родителей (класса) был бы определенный способ, которым вы должны были себя вести в общем смысле. Но когда твой дядя (декоратор) возил тебя по работе, твое поведение было бы другим, чем дома. То, что от вас ожидалось, и, следовательно, то, как вы действовали, было другим, пока вы находились под временной опекой другого родственника, чем ваши родители.

Например, давайте взглянем на несколько декораторов, которые изменяют поведение HTML-формы. Форма написана следующим образом и отображается в веб-фреймворке Flask:

@app.route('/')
def styling():
    return '''
            <form action='' method='post'>
                <dl>
                  <dt>Username:
                  <dd><input type=text name=username>
                  <dt>Password:
                  <dd><input type=text name=password>
                  <dd><input type=submit value=Login>
                </dl>
            </form>'''

и рендерится вот так

Теперь давайте добавим в смесь следующие декораторы:

def style_with_flex_end(func):
    def func_wrapper():
        return '''<div style="display: flex; flex-direction: row; justify-content: flex-end">{0}</div>'''.format(func())
    return func_wrapper
def style_with_flex_center(func):
    def func_wrapper():
        return '''<div style="display: flex; flex-direction: row; justify-content: center">{0}</div>'''.format(func())
    return func_wrapper

Оба декоратора делают похожие вещи. Единственная разница между ними заключается в теге стиля justify-content. Однако, просто задав вышеупомянутой HTML-форме контекст, применяемый этими декораторами, мы получим другой результат. Давайте присоединим декоратор style_with_flex_end к этому маршруту Flask, просто вызвав его с помощью символа «@» и без круглых скобок над определением функции «стилизации».

@app.route('/')
@style_with_flex_end
def styling():
    return '''
            <form action='' method='post'>
                <dl>
                  <dt>Username:
                  <dd><input type=text name=username>
                  <dt>Password:
                  <dd><input type=text name=password>
                  <dd><input type=submit value=Login>
                </dl>
            </form>'''

Добавление декоратора отображает ту же форму следующим образом:

Как видите, контекст justify-content: flex-end;, предоставленный декоратором, изменился в том месте, где форма появлялась на странице, без необходимости изменять код самой формы. То же самое можно сделать с другим нашим декоратором:

@app.route('/')
@style_with_flex_center
def styling():
    return '''
            <form action='' method='post'>
                <dl>
                  <dt>Username:
                  <dd><input type=text name=username>
                  <dt>Password:
                  <dd><input type=text name=password>
                  <dd><input type=submit value=Login>
                </dl>
            </form>'''

который затем отображается следующим образом:

Таким образом, декоратор добавляет контекст к функции. Это, по сути, дает вашей функции «разрешение» использовать добавленную декорацию, которую предоставляет декоратор. Наш пример показывает, что если мы спросим у @style_with...decorators, может ли реализованный ими стиль justify-content «выйти в игру», они дадут нашему HTML другой набор ожиданий, чем когда они «дома с мамой и папой», поэтому позволяя вести себя не так, как обычно.

Я надеюсь, что это прояснило некоторые вещи для тех из вас, кто не знал, что такое декоратор и что он делает. Пожалуйста, оставьте комментарий ниже, если у вас есть какие-либо вопросы или вам нужны разъяснения! Я всегда готов помочь другим понять, потому что, как всегда, миру нужны вы, ваши идеи и ваш уникальный подход к этой замечательной области, чтобы мы все могли создавать крутые, полезные, потрясающие технологии завтрашнего дня!