Меню

Python конвертеры для полезных нагрузок

Увеличиваем скорость работы Python до уровня C++ с Numba

Авторизуйтесь

Увеличиваем скорость работы Python до уровня C++ с Numba

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

Прим. ред. Это перевод. Мнение редакции может не совпадать с мнением автора оригинала.

Тест базовой скорости

Для сравнения базовой скорости Python и C++ я буду использовать алгоритм генерации случайных простых чисел.

Блок-схема алгоритма генерации простых чисел

Реализация на Python

Реализация на C++

Результат

  • Python: скорость выполнения 80,137 секунд;
  • C++: скорость выполнения 3,174 секунды.

Комментарий

Как и ожидалось, программа на C++ выполняется в 25 раз быстрее, чем на Python. Ожидания подтвердились, потому что:

  • Python — это динамически типизированный язык;
  • GIL(Global Interpreter Lock) — не поддерживает параллельное программирование.

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

Numba

Numba — это Open Source JIT-компилятор, который переводит код на Python и NumPy в быстрый машинный код.

Чтобы начать использовать Numba, просто установите её через консоль:

Реализация на Python с использованием Numba

Как вы могли заметить, в коде добавились декораторы njit:

  • parallel=True — включает параллельное выполнение программы на процессоре;
  • fastmath=True — разрешает использование небезопасных преобразований с плавающей точкой;
  • cache=True — позволяет сократить время компиляции функции, если она уже была скомпилирована.

Итоговая скорость Python

  • Python: скорость выполнения 1,401 секунды;
  • C++: скорость выполнения 3,174 секунды.

Теперь вы знаете что Python способен обогнать C++. О других способах увеличения скорости работы Python читайте в статье про пять проектов, которые помогают ускорить код на Python.

Хинт для программистов: если зарегистрируетесь на соревнования Huawei Cup, то бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.

Перейти к регистрации

Источник

4. Конвертация типов данных

Функция type() возвращает тип объекта. Ее назначение очевидно, и на примерах можно понять, зачем эта функция нужна.

Также в этом материале рассмотрим другие функции, которые могут помочь в процессе конвертации типа данных. Некоторые из них — это int() , float() или str() .

type() — это базовая функция, которая помогает узнать тип переменной. Получившееся значение можно будет выводить точно так же, как обычные значения переменных с помощью print .

Где используется

  • Функция type() используется для определения типа переменной.
  • Это очень важная информация, которая часто нужна программистам.
  • Например, программа может собирать данные, и будет необходимость знать тип этих данных.
  • Часто требуется выполнять определенные операции с конкретными типами данных: например, арифметические вычисления на целых или числах с плавающей точкой или поиск символа в строках.
  • Иногда будут условные ситуации, где с данными нужно будет обращаться определенным образом в зависимости от их типа.
  • Будет и много других ситуаций, где пригодится функция type() .

Рекомендации по работе с типами данных

  1. Типы int и float можно конвертировать в str , потому что строка может включать не только символы алфавита, но и цифры.
  2. Значения типа str всегда представлены в кавычках, а для int , float и bool они не используются.
  3. Строку, включающую символы алфавита, не выйдет конвертировать в целое или число с плавающей точкой.
Читайте также:  Как обозначаются все полезные ископаемые

Примеры конвертации типов данных

В следующих материалах речь пойдет о более сложных типах данных, таких как списки, кортежи и словари. Их обычно называют составными типами данных, потому что они могут состоять из значений разных типов. Функция type() может использоваться для определения их типов также.

Функция int()

С помощью функции int() можно попробовать конвертировать другой тип данных в целое число.

В следующем примере можно увидеть, как на первой строке переменной inc_count присваивается значение в кавычках.

Из-за этих кавычек переменная регистрирует данные как строку. Дальше следуют команды print для вывода оригинального типа и значения переменной, а затем — использование функции int() для конвертации переменной к типу int .

После этого две функции print показывают, что значение переменной не поменялось, но тип данных — изменился.

Можно обратить внимание на то, что после конвертации выведенные данные не отличаются от тех, что были изначально. Так что без использования type() вряд ли удастся увидеть разницу.

Источник

Конвертеры маршрутов в Django 2.0+ (path converters)

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

Меня зовут Александр Иванов, я наставник в Яндекс.Практикуме на факультете бэкенд-разработки и ведущий разработчик в Лаборатории компьютерного моделирования. В этой статье я расскажу о конвертерах маршрутов в Django и покажу преимущества их использования.

Первое, с чего начну, — границы применимости:

  1. версия Django 2.0+;
  2. регистрация маршрутов должна выполняться с помощью django.urls.path .

Итак, когда к Django-серверу прилетает запрос, он сперва проходит через цепочку middleware, а затем в работу включается URLResolver (алгоритм). Задача последнего — найти в списке зарегистрированных маршрутов подходящий.

Для предметного разбора предлагаю рассмотреть следующую ситуацию: есть несколько эндпоинтов, которые должны формировать разные отчёты за определённую дату. Предположим, что эндпоинты выглядят так:

Как бы могли выглядеть маршруты в urls.py ? Например, так:

Каждый элемент в является параметром запроса и будет передан в обработчик.

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

Тогда в каждом обработчике был бы примерно такой код (обращайте внимание на аннотации типов):

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

А в каждом обработчике тогда будет просто вызов этой вспомогательной функции:

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

Но на самом деле вот что я сделал: я переложил часть ответственности по решению, должен ли этот обработчик запускаться для этого маршрута или нет, с URLResolver на сам обработчик. Выходит, что URLResolver некачественно проделал свою работу, и моим обработчикам приходится не только заниматься полезной работой, но и решать, а должны ли они вообще этим заниматься. Это явное нарушение принципа единственной ответственности из SOLID. Так не пойдёт. Надо исправляться.

Читайте также:  Золотой ус цветок для чего полезный

Стандартные конвертеры

Django предоставляет стандартные конвертеры маршрутов. Это механизм определения, подходит ли часть маршрута или нет самим URLResolver. Приятный бонус: конвертер может менять тип параметра, а значит, в обработчик может прийти сразу нужный нам тип, а не строка.

Конвертеры указываются перед названием параметра в маршруте через двоеточие. На самом деле конвертер есть у всех параметров, если он не указан явно, то по умолчанию используется конвертер str .

Осторожно: некоторые конвертеры выглядят как типы в Python, поэтому может показаться, что это обычные приведения типов, но это не так — например, нет стандартных конвертеров float или bool . Позднее я покажу, что из себя представляет конвертер.

После просмотра стандартных конвертеров, становится очевидно, что для id стоит использовать конвертер int :

Но как быть с датой? Стандартного конвертера для неё нет.

Можно, конечно, извернуться и сделать так:

Действительно, часть проблем удалось устранить, ведь теперь гарантируется, что дата будет отображаться тремя числами через дефисы. Однако всё ещё придётся обрабатывать проблемные случаи в обработчике, если клиент передаст некорректную дату, например 2021-02-29 или вообще 100-100-100. Значит, этот вариант не подходит.

Создаём свой конвертер

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

Для этого надо сделать два шага:

  1. Описать класс конвертера.
  2. Зарегистрировать конвертер.

Класс конвертера — это класс с определённым набором атрибутов и методов, описанных в документации (на мой взгляд, несколько странно, что разработчики не сделали базовый абстрактный класс). Сами требования:

  1. Должен быть атрибут regex , описывающий регулярное выражение для быстрого поиска требуемой подпоследовательности. Чуть позже покажу, как он используется.
  2. Реализовать метод def to_python(self, value: str) для конвертации из строки (ведь передаваемый маршрут — это всегда строка) в объект python, который в итоге будет передаваться в обработчик.
  3. Реализовать метод def to_url(self, value) -> str для обратной конвертации из объекта python в строку (используется, когда вызываем django.urls.reverse или тег url ).

Класс для конвертации даты будет выглядеть так:

Я противник дублирования, поэтому формат даты вынесу в атрибут — так и поддерживать конвертер проще, если вдруг захочу (или потребуется) изменить формат даты:

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

Вот теперь можно описать маршруты в urls.py (я специально сменил название параметра на dt , чтобы не сбивала с толку запись date:date ):

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

Выглядит потрясающе! И это так, можно проверять.

Под капотом

Если посмотреть внимательно, то возникает интересный вопрос: нигде нет проверки, что дата корректна. Да, есть регулярка, но под неё подходит и некорректная дата, например 2021-01-77, а значит, в to_python должна быть ошибка. Почему же это работает?

Про такое я говорю: «Играй по правилам фреймворка, и он будет играть за тебя». Фреймворки берут на себя ряд стандартных задач. Если же фреймворк чего-то не может, то хороший фреймворк предоставляет возможность расширить свой функционал. Поэтому не стоит заниматься велосипедостроением, лучше посмотреть, как фреймворк предлагает улучшить собственные возможности.

Читайте также:  Упаковка как полезная модель

У Django есть подсистема маршрутизации с возможностью добавления конвертеров, которая берёт на себя обязанности по вызову метода to_python и отлавливания ошибок ValueError .

Привожу код из подсистемы маршрутизации Django без изменений (версия 3.1, файл django/urls/resolvers.py , класс RoutePattern , метод match ):

Первым делом производится поиск совпадений в переданном от клиента маршруте с помощью регулярного выражения. Тот самый regex , что определен в классе конвертера, участвует в формировании self.regex , а именно подставляется вместо выражения в угловых скобках <> в маршруте.

Например, превратится в

В конце как раз та самая регулярка из DateConverter .

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

Для каждого параметра имеется свой конвертер, который и используется для вызова метода to_python . И вот здесь самое интересное: вызов to_python обёрнут в try/except , и отлавливаются ошибки типа ValueError . Именно поэтому и работает конвертер даже в случае некорректной даты: валится ошибка ValueError , и это расценивается так, что маршрут не подходит.

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

Не стоит останавливаться

Кажется, что всё отлично, конвертеры работают, в обработчики сразу приходят нужные типы… Или не сразу?

В обработчике для формирования отчёта наверняка нужен именно User , а не его id (хотя и такое может быть). В моей гипотетической ситуации для создания отчёта нужен как раз именно объект User . Что же тогда получается, опять двадцать пять?

Снова перекладывание обязанностей на обработчик.

Но теперь понятно, что с этим делать: писать свой конвертер! Он убедится в существовании объекта User и передаст его в обработчик.

После описания класса регистрирую его:

И наконец описываю маршрут:

Конвертеры для моделей могут использоваться часто, поэтому удобно сделать базовый класс такого конвертера (заодно добавил проверку на существование всех атрибутов):

Тогда описание нового конвертера в модель сведётся к декларативному описанию:

Итоги

Конвертеры маршрутов — мощный механизм, который помогает делать код чище. Но появился этот механизм только во второй версии Django — до этого приходилось обходиться без него. Отсюда и взялись вспомогательные функции типа get_object_or_404 , без этого механизма сделаны крутые библиотеки вроде DRF.

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

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

Источник