Люди, которые впервые сталкиваются с разработкой приложений Flask, часто интересуются, что такое декораторы. Это – одна из фундаментальных тем, которые необходимо изучить для того, чтобы полноценно разрабатывать различные программные решения.
Декораторы – это одна из особенностей фреймворка Flask, который используется для разработки веб-приложений. С их помощью можно выполнять целый спектр задач, начиная определением маршрутов и заканчивая обработкой ошибок при создании веб-программ. Поэтому они получили широкое распространение в среде разработчиков.
Декораторы делают код программы лаконичным и понятным. Именно это служит причиной, почему подавляющее число расширений Flask и иных пакетов в Python используют декораторы для того, чтобы увеличить количество функций, доступных в них.
Сегодня мы поговорим о том, как включать декораторы в приложения, как их создавать, что они делают. Прежде всего, поговорим о том, как осуществляется регистрация функции с помощью декораторов в Python. Начинаем.
Использование декораторов в Python для регистрации функции
Наиболее легкая для использования разновидность декораторов применяется в использования функции в качестве обработчика события. Распространение этого шаблона в Python-приложениях стало очень широким. Это не удивительно, учитывая то, что он дает двум или большему количеству подсистем взаимодействовать друг с другом, не имея никакой информации.
Этот процесс обрел известность как «несвязанного» дизайна. Структура декоратора регистрации функции без аргументов следующая.
def request_logger(f): # ... # здесь вставляются действия настраиваемого декоратора # ... return f
Здесь функция request_logger – это имя декоратора. С его помощью Python будет в дальнейшем понимать, что нужно обращаться именно к нему. Этот декоратор не имеет аргументов. Тем не менее, здесь мы использовали аргумент f.
В ходе работы с декораторами требуется отдельно выполненная имплементация (как указано ранее) от использования. Для получения более широкого представления, давайте ознакомимся еще с одним примером того, как декоратор будет использоваться.
@request_logger def log_a_request(request): pass
Теперь давайте проанализируем код, приведенный только что. Обратите внимание, что у декоратора отсутствуют аргументы, но и в этом случае мы таки передали аргумент декоратору. Почему? Дело в том, что этот аргумент действительно запрашивается. А все из-за того, что Python передает функцию декоратора в качестве аргумента.
Значительно проще описать то, как это работает, с помощью небольшого фрагмента кода. Он позволяет добиться такого же результата, но не используя декораторы.
def log_a_request(request): pass log_a_request = request_logger(log_a_request)
Если описанный в этом примере аргумент использовать в качестве руководства, то вы обнаружите, что вызов функции декоратора осуществляется вместе с декорированной функцией в качестве аргумента. Что касается возвращаемого значения, то это функция, которая занимает место оригинальной (которая был нами декорирована в предыдущих примерах). Что это дает? Прежде всего, предоставляет альтернативу, что всегда хорошо в программировании. Она используется в продвинутых декораторах.
А вот более простые разновидности, которые рассматриваются здесь, не нуждаются в этом. Поэтому их окончание просто является инструкцией return f, чтобы изначальная функция осталась в неизменном виде.
Очень часто для этого типа декоратора регистрации функции используется сохранение ссылки на декорированную функцию. После того, как случится предоставленное декоратором событие, ее можно вызвать.
Далее вы увидите то, как выполнена реализация декоратора request_logger на практике.
Здесь необходимо понимать, что декоратора осуществляет регистрацию одной либо нескольких функций, используемых для регистрации запросов. Фактически задача декоратора в этом примере – лишь добавить декорированную функцию в список.
all_request_loggers = [] def request_logger(f): all_request_loggers.append(f) return f
Этот пример показывает то, как декоратор применяется к функции, которая нередко располагается в другой части программы.
@request_logger def log_a_request(request): print(request.method, request.path)
Если декоратор используется таким образом, это приведет к тому, что функция декоратора request_logger(), которая была нами ранее определена, будет выполняться совместно с log_a_request, передаваемой в качестве аргумента f. После этого функция декоратора осуществит сохранение ссылки на эту функцию в глобальном списке all_request_loggers. Если имеются другие функции, декорирование которых было осуществлено с использованием этого декоратора, они будут добавляться в соответствующий список. То есть, в all_request_loggers.
Что касается последней части данной реализации, то за счет нее происходит вызов всех функций, которые используются в качестве регистраторов запросов, когда происходит события. Вообще, сами события могут быть абсолютно разными. Но в этом случае в качестве такого выступает получение запроса. Это реализуется с помощью стандартного цикла for в отдельной функции.
def invoke_request_loggers(request): for logger in all_request_loggers: logger(request)
Давайте теперь приведем то, как будет реализована полная имплементация этого примера.
from flask import Flask, request app = Flask(__name__) all_request_loggers = [] def request_logger(f): all_request_loggers.append(f) return f def invoke_request_loggers(request): for logger in all_request_loggers: logger(request) @request_logger def log_a_request(request): print(request.method, request.path) @app.route('/') def index(): invoke_request_loggers(request) return 'Hello World!'
Может появиться вопрос: а зачем выполнять столько действий, если достаточно просто напрямую вызвать функцию log_a_request()? В ряде случаев, например, в более простых программах, эта идея может быть хорошей. Тем не менее, применение декоратора дает программе возможность отделить функцию представления от функции или функций, которые были зарегистрированными для того, чтобы выполнять регистрацию запросов. И такая практика является очень хорошим стандартом. Вот, почему нужно так усложнять, на первый взгляд. В дальнейшем можно значительно упростить работу с приложением.
Вообще, в программировании такие ситуации бывают довольно часто. Нередко приходится писать дополнительные строки кода или делать то, что на первый взгляд кажется сложнее. Тем не менее, эти, ненужные на первый взгляд действия, позволяют значительно упростить разработку в дальнейшем.
В примере, который был нами приведен ранее (где демонстрируется работа с функцией представления index()) вообще не надо знать ничего о функциях логирования запросов, а также об их количестве. Более того, нет необходимости знать, а вообще они есть, или нет. Возможна регистрация различных функций логирования в деплое еще на этапе продакшена, а не во время локальной разработки. С помощью такой реализации приложения можно добиться чистоты кода и независимости от того, как осуществляется логирование запроса.
В реальной программе такие функции будут расположены в собственном модуле, отдельно от других компонентов. Это позволит любому модулю осуществить импорт декоратора request_logger и применять его в случае необходимости регистрации логирования запросов. Аналогично, в других частях приложения, также есть возможность вызова функции invoke_request_loggers().
Шаблон проектирования Наблюдатель
Если вы знакомы с шаблоном проектирования Наблюдатель (то бишь, Observer), то вы могли заметить, что в этой статье заложены очень похожие идеи. Собственно, этот шаблон действительно можно использовать в некоторых ситуациях. Например, в таких случаях это разумно:
- Если создается игра. В этом случае можно осуществить регистрацию обработчиков событий, вызываемых в процессе столкновения двух спрайтов.
- При разработке прикладного приложения. Здесь если программа ничего не делает, можно осуществить регистрацию обработчиков обновления фона.
- В программе, выполняющейся в командной строке. В этом случае можно осуществить регистрацию функции обработчика ошибок (или нескольких), вызываемых для очистки после возникновения непредвиденных ситуаций.
- Что касается разработки веб-приложений, то можно привести некоторые примеры из flask . Декораторы route, before_request, after_request и teardown_request применяют этот шаблон. Конечно, они сложнее показанного выше. Ряд из них могут принимать аргументы, а другие адаптируются под программу и то, какие функции возвращаются. Но они требуют отдельного рассмотрения.
Заключение
Таким образом, мы разобрались в том, как осуществлять регистрацию функций и событий во Flask. Конечно, это далеко не все аспекты, которые нужно рассмотреть. Есть еще много дополнительных нюансов. Также есть более сложные декораторы типа тех, которые мы рассмотрели немного выше. Тем не менее, в них разобраться новичку будет довольно непросто. Поэтому пока тщательно изучите приведенную в этой статье информацию, а потом уже приступайте к следующим этапам. Без хорошей фундаментальной базы невозможно создание сложных приложений. Поэтому не стоит ее недооценивать.
Декораторы взаимодействуют с функциями таким образом, чтобы работа над разработкой приложения была значительно проще. И это главное.