fsmcontext python что это

Конечные автоматы (FSM)¶

Теория¶

В этой главе мы поговорим о, пожалуй, самой важной возможности ботов: о системе диалогов. К сожалению, далеко не все действия в боте можно выполнить за одно сообщение или команду. Предположим, есть бот для знакомств, где при регистрации нужно указать имя, возраст и отправить фотографию с лицом. Можно, конечно, попросить пользователя отправить фотографию, а в подписи к ней указать все данные, но это неудобно для обработки и запроса повторного ввода.
Теперь представим пошаговый ввод данных, где в начале бот «включает» режим ожидания определённой информации от конкретного юзера, далее на каждом этапе проверяет вводимые данные, а по команде /cancel прекращает ожидать очередной шаг и возвращается в основной режим. Взгляните на схему ниже:

Зелёной стрелкой обозначен процесс перехода по шагам без ошибок, синие стрелки означают сохранение текущего состояния и ожидание повторного ввода (например, если юзер указал, что ему 250 лет, следует запросить возраст заново), а красные показывают выход из всего процесса из-за команды /cancel или любой другой, означающей отмену.

Процесс со схемы выше в теории алгоритмов называется конечным автоматом (или FSM — Finite State Machine). Подробнее об этом можно прочесть здесь.

Практика¶

В качестве примера мы напишем имитатор заказа еды и напитков в кафе, а заодно научимся хранить логически разные хэндлеры в разных файлах.

Примечание об исходных текстах к главе

В тексте будет рассмотрен не весь код бота, некоторые импорты и обработчики специально пропущены для улучшения читабельности. Полный набор исходников можно найти на GitLab или в зеркале на GitHub.

За основу структуры файлов и каталогов взят репозиторий tgbot_template от пользователя Tishka17. В этой главе будет рассмотрен сильно упрощённый вариант его примера, а далее по мере усложнения бота структура файлов будет расширяться.
Спасибо!

Структура файлов и каталогов¶

О модулях, пакетах и каталогах

Создание шагов¶

Рассмотрим описание шагов для «заказа» еды. Для начала в файле app/handlers/food.py импортируем необходимые объекты и приведём списки блюд и их размеров (в реальной жизни эта информация может динамически подгружаться из какой-либо БД):

Напишем обработчик первого шага, реагирующий на команду /food (регистрировать его будем позднее):

Осталось реализовать последнюю функцию, которая отвечает за получение размера порции (с аналогичной проверкой ввода) и вывод результатов пользователю:

Шаги для выбора напитков делаются совершенно аналогично. Попробуйте сделать самостоятельно или загляните в исходные тексты к этой главе.

Общие команды¶

Раз уж заговорили о сбросе состояний, давайте в файле app/handlers/common.py реализуем обработчики команды /start и действия «отмены». Первая должна показывать некий приветственный/справочный текст, а вторая просто пишет «действие отменено». Обе функции сбрасывают состояние и данные и убирают обычную клавиатуру, если вдруг она есть:

Зарегистрируем эти два обработчика:

Точка входа¶

Теперь, вооружившись знаниями о конечных автоматах, вы можете безбоязненно писать ботов с системой диалогов.

Источник

Урок 3. Машина состояний и то самое логгирование

Урок проводится с использованием aiogram версии 1.2

Сегодня мы научимся использовать:

Традиционно код урока доступен на GitHub

Создаем состояния

Так как мы сейчас будем разбирать машину состояний, эти самые состояния необходимо сначала создать. Сперва в голову приходит использование енумов, однако я предпочел воспользоваться встроенным классом Helper, который подходит для данной задачи даже лучше.

Итак, запишем в файл utils.py наш демонстрационный класс с состояниями:

Ещё не забываем добавить в config.py токен своего бота и мы готовы писать логику!

Указываем хранилище состояний и включаем логгирование

К привычным с прошлых уроков импортам у нас добавляется ещё парочка, а именно:

И тут же применяем их:

На первой строчке мы указали хранилище состояний в оперативной памяти, так как потеря этих состояний нам не страшна (да и этот вариант больше всего подходит для демонстрационных целей, так как не требует настройки). Однако если у вас от состояний что-то зависит, рекомендуется ипользовать более надеждное хранилище. На данный момент можно подключить Redis и RethinkDB.

Обрабатываем входящие сообщения

Итак, по традиции добавляем обработчики команд start и help :

Читайте также:  что делать если котенок ест наполнитель комкующийся

А так же «ловим» все сообщения, отправленные при «нулевом» состоянии:

Переходим к главной теме нашего урока: состояниям

Эти самые состояния нужно как-то устанавливать, поэтому сделаем так:

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

Теперь отрабатываем входящие сообщения при выбранном состоянии

Теперь добавим такой хэндлер:

Библиотека сама понимает, когда мы передаем список состояний, а когда только одно состояние и под капотом обрабатывает их по-разному, но нам нет смысла об этом задумываться. В этом плане мы в плюсе, так как можем сделать вот так:

Ну и последний на сегодня хэндлер:

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

Для красоты ещё стоит закрывать соединение с хранилищем состояний, для этого объявляем функцию:

Источник

Контекстные Менеджеры в Python

Несколько лет назад, в Python 2.5 добавили новое ключевое слово, под названием оператор with. Это новое ключевое слово позволяет разработчику создавать контекстные менеджеры. Но подождите, что же такое контекстный менеджер? Это удобные конструкции, которые позволяют разработчику настраивать что-нибудь и разрывать в автоматическом режиме. Например, вам может потребоваться открыть файл, вписать в него кучу всего и закрыть. Это классический пример работы контекстного менеджера. Фактически, Python создает один такой экземпляр автоматически каждый раз, когда вы открываете файл, используя оператор with:

В Python 2.4, вам нужно делать это старомодным способом:

Это работает путем использования двух волшебных методов Python: __enter__ и __exit__. Давайте попробуем создать собственный контекстный менеджер, чтобы увидеть, как это работает на практике.

Создаем класс Context Manager

Вместо того, чтобы переписывать открытый метод Python, мы создадим контекстный менеджер, который создает связь с базой данных SQLite, и закрывает её по окончанию работы. Вот простой пример:

В данном коде мы создали класс, который берет путь к файлу базы данных SQLite Python. Метод __enter__ выполняется автоматически, он создает и возвращает объект связи базы данных. Теперь мы можем создать курсор для записи в базу данных или чтобы её запросить. Когда мы выходим из оператора with, метод __exit__ запускается, закрывая таким образом связь. Давайте попробуем создать контекстный менеджер при помощи другого метода.

Создание контекстного менеджера с использованием contextlib

В Python 2.5 добавили не только оператор with, но также модуль contextlib. Это позволяет нам создать контекстный менеджер, используя функцию модуля contextlib под названием contextmanager в качестве декоратора. Давайте попробуем создать контекстный менеджер который открывает и закрывает файл после проделанной в нем работе:

Здесь мы просто импортируем contextmanager из contextlib и декорируем нашу функцию file_open с ним. Это позволяет нам вызвать file_open используя оператор with. В нашей функции мы открываем файл, отдаем его, чтобы функция calling могла использовать его. После того, как оператор закончит, контроль возвращается обратно к функции file_open, которая продолжает следовать по коду за вызываемым оператором. Это приводит оператор finally к исполнению, благодаря которому и закрывается файл. Если возникла ошибка OSError во время работы с файлом, она будет выявлена и оператор finally закроет обработчик файлов несмотря на это.

contextlib.closing()

Модуль contextlib содержит несколько полезных утилит. Первая – это класс closing, который закроет объект по завершению определенного блока кода. В документации Python есть пример кода, похожий на следующий:

В целом, мы создаем закрывающую функцию, которая завернута в контекстный менеджер. Это эквивалент того, что делает класс closing. Но есть небольшая разница: вместо декоратора, мы можем использовать класс class в нашем операторе with. Давайте взглянем:

В данном примере мы открыли страницу URL, но обернули её в наш класс closing. Это приведет к закрытию дескриптора веб-страницы, сразу после выхода из блока кода оператора with.

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Читайте также:  coaxial spdif что это

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

contextlib.suppress(*exceptions)

Еще один полезный инструмент — класс suppress, который был добавлен в Python 3.4. Идея в том, что данная утилита контекстного менеджера может подавлять любое количество исключений. Скажем, нам нужно проигнорировать исключение FileNotFoundError. Если прописать следующий контекстный менеджер, то это не сработает:

Как мы видим, этот контекстный менеджер не выполняет обработку данного исключения. Если вам нужно проигнорировать эту ошибку, лучше напишите следующий код:

Здесь мы импортируем suppress и передаем его исключению FileNotFoundError. Если вы запустите этот код, вы увидите, что ничего не происходит, так как файл не существует, но и ошибка не возникает. Обратите внимание на то, что этот контекстный менеджер является реентрабельным, но об этом позже.

contextlib.redirect_stdout / redirect_stderr

Библиотека contextlib содержит несколько замечательных инструментов для перенаправления stdout и stderr, которые появились в Python 3.4 и 3.5 соответственно. До того, как эти инструменты появились, и когда вам нужно перенаправить stdout, вам нужно сделать что-то на подобии этого:

С модулем contextlib вы можете сделать следующее:

В обоих примерах мы перенаправили stdout к файлу. Когда мы вызываем справку Python, вместо вывода в stdout, она сохраняется непосредственно в файле. Вы также можете перенаправить stdout в какой-нибудь буфер или текстовый инструмент управления из арсенала пользовательского интерфейса, вроде Tkinter или wxPython.

ExitStack

ExitStack – это контекстный менеджер, который позволит вам легко комбинировать другие контекстные менеджеры, а также функции очистки. Звучит немного запутанно, на первый взгляд, так что давайте рассмотрим простой пример из документации Python, с его помощью будет проще уловить суть:

В общем и целом, данный код создает серию контекстных менеджеров внутри списка. ExitStack поддерживает стек регистрируемых колбеков, которые вызываются в обратом порядке когда экземпляр закрыт, что и происходит, когда мы выходим из части the оператора with. В документации Python существует великое множество метких примеров работы contextlib, где вы можете ознакомиться с такими темами как:

Я настоятельно рекомендую ознакомиться с этими темами, так как вы поймете, насколько эффективным и полезным может быть этот класс.

Реентерабельные контекстные менеджеры

Большая часть создаваемых вами контекстных менеджеров может быть написана только для использования с оператором with для одноразового применения. Вот пример:

Здесь мы создали экземпляр контекстного менеджера и пытаемся запустить его дважды с оператором with. Второй запуск приводит к ошибке RuntimeError. Но что делать, если нам необходимо, чтобы контекстный менеджер запускался дважды? Для этой цели нам и нужен реентрабельный контекстный менеджер. Давайте используем менеджер redirect_stdout, который мы применяли ранее.

Здесь мы создали вложенные контекстные менеджеры, которые оба пишут в StringIO, который является текстовым потоком в памяти. Причина, по которой это работает, а не приводит к ошибке RuntimeError, как было ранее в том, что redirect_stdout является реентрабельным и позволяет нам вызывать его дважды. Конечно, ситуации в реальной жизни могут быть заметно сложнее, когда мы работаем с большим количеством функций, которые вызывают друг друга. Пожалуйста, обратите также внимание на то, что контекстные менеджеры не обязательно являются защищенными от потоков. Обратитесь к документации, перед тем как использовать их в потоках во избежание путаницы.

Подведем итоги

Контекстный менеджер – крайне полезный и удобный инструмент, способный выручить во многих ситуациях. Я пользуюсь ими в своих автоматических тестах постоянно, для открытия и закрытия диалогов, например. Теперь вы можете использовать ряд встроенных в Python инструментов для создания собственных контекстных менеджеров. Убедитесь в том, что вы выделили достаточно времени для изучения документации Python о contextlib, так как в ней хранится очень много дополнительной полезной информации, которая не была рассмотрена в данной статье.

Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.

Читайте также:  какой круг выбрать для ребенка 3 лет

E-mail: vasile.buldumac@ati.utm.md

Образование
Universitatea Tehnică a Moldovei (utm.md)

Источник

Как устроен with

Контекстные менеджеры (оператор with ) встречаются там, где перед совершением действия нужно что-то настроить, а после – прибраться. Например, чтобы прочитать файл, мы используем такой контекстный менеджер:

Здесь “настройкой” является открытие файла, а “уборкой” – его закрытие. Сейчас мы разберемся, как утроены контекстные менеджеры в Питоне.

Свой контекстный менеджер

Заменим нетипичную конструкцию with. as более распространенной try. finally :

Напишем собственный контекстный менеджер, который создаст книгу в Excel, а по завершении работы с ней – сохранит. Другими словами, мы хотели бы использовать его вот так:

Соответственно, использовать его можно так:

И этот код действительно создает книгу и записывает число 1 в первую ячейку. Проблема только в том, что так контекстные менеджеры не пишут.

Свой контекстный менеджер, ver. 2.0

Теперь заметим, что генератор create_workbook содержит всю предметно-ориентированную логику. Поэтому класс CreateWorkbook можно обобщить так, чтобы он от нее не зависил и работал с любыми генераторами. Заодно переименуем его в ContextManager :

В конструкторе ContextManager мы принимаем генератор, при вызове объекта записываем переданные генератору параметры, а при вызове __enter__ вызываем генератор. Теперь класс ContextManager можно использовать следующим образом:

Вызов ContextManager(create_workbook)(‘workbook_name.xlsx’) выглядит некрасиво. Исправим это:

И это еще не всё. Если после yield workbook пользовательский код поднимит исключение, мы не сохраним книгу. Чтобы этого избежать, обернем это выражение в try. finally :

А вот так контекстные менеджеры писать принято.

Резюме

Дальнейшее чтение

Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.

Источник

Использование менеджера контекста в Python

В этой статье вы узнаете что такое контекстный менеджер, и как он упрощает работу в Python.

Введение

Контекстный менеджер помогает упростить некоторые общие шаблоны управления ресурсами, абстрагируя их функциональность и позволяя их учитывать и повторно использовать. Давайте разберемся в упрощенном варианте.

Причины использования

Программисты время от времени работают с внешними ресурсами, такими как файлы, соединения с базами данных, блокировки и так далее. Контекстные менеджеры позволяют нам управлять этими ресурсами, указывая:

Рассмотрим следующий пример:

Обратите внимание, что я вызываю метод close(), чтобы гарантировать, что файловый дескриптор освобождается каждый раз. Если бы я этого не сделал, наша ОС (операционная система) в конечном итоге исчерпала бы свой разрешенный лимит на открытие файловых дескрипторов.

Однако я напишу более удобочитаемый вариант с помощью контекстного менеджера:

В этом примере open(«temp.txt», «+a») является менеджером контекста, который активируется с помощью оператора with. Обратите внимание, что мне не нужно было явно закрывать документ, контекстный менеджер позаботился об этом за меня. Точно так же в Python есть и другие предопределенные контекстные менеджеры, которые облегчают нашу работу.

Создание менеджера контекста

Существует два способа определения пользовательского контекстного менеджера:

Контекстный менеджер на основе класса

Давайте продолжим с нашим примером и попробуем определить наш собственный контекстный менеджер, который будет эмулировать функцию open():

Обработка ошибок

Пример того как я обрабатываю FileNotFoundError:

Это базовый код обработки ошибок, который должен быть каждый раз, когда вы открываете файл. Давайте попробуем добавить его в наш созданный менеджер контекста:

Изменения в атрибутах метода _exit_:

Контекстные менеджеры на основе функции

Управление контекстом на основе функций осуществляется с помощью библиотеки под названием contextlib, с помощью которого мы можем превратить простую функцию-генератор в контекстный менеджер. Вот как выглядит типичный код:

Обработка файлов

Я заключаю yield в блок try, потому что не знаю, что пользователь собирается делать с объектом open_file.

Заключение

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

Источник

Сказочный портал