Обзор
В этом руководстве будет показано, как создать гибридный пакет Python / C ++ с использованием setuptools, системы сборки Python и CMake, системы сборки, используемой во многих проектах C / C ++.
Репозиторий GitHub, который прилагается к этому руководству, можно найти здесь. Мы создадим простой пакет, который позволит нам вычислять технические индикаторы, обычно используемые в платформах автоматической торговли акциями.
Прежде чем двигаться дальше, вам может быть интересно: какова цель создания гибридного пакета Python / C ++? Какие преимущества он предлагает по сравнению с обычным пакетом Python?
Во-первых, Python - это потрясающий язык программирования. Python де-факто является языком компьютерного зрения, машинного обучения и анализа данных. Кроме того, он отлично подходит для веб-разработки и быстрого прототипирования, а также для других задач. Однако для задач, требующих тяжелых вычислений, лучше подходят скомпилированные языки программирования, такие как C ++.
Технические индикаторы обычно вычисляются на основе списков данных о запасах, которые могут включать тысячи (или более) точек данных. Кроме того, автоматические торговые платформы должны быстро вычислять эти индикаторы, чтобы принимать точные решения о покупке и продаже акций определенной акции. По этой причине мы будем реализовывать функцию технического индикатора на C ++.
Однако то, что мы решили реализовать функцию на C ++, не означает, что мы должны реализовать всю платформу для торговли акциями на C ++. Мы можем создать библиотеку C ++ с нашей функцией технического индикатора, создать привязки Python для библиотеки, а затем вызвать функцию, как если бы это была любая другая функция Python. Фактически, именно так работают такие пакеты, как NumPy и TensorFlow: основная логика реализована на C ++, и вы взаимодействуете с этой логикой через привязки Python.
Сборка пакета
Продолжая, давайте рассмотрим структуру нашего репозитория:
|DolphinTradiningIndicators |- docs/ |- lib/ |- - catch2/ |- - - catch.hh |- - indicators/ |- - - include/ |- - - - indicators_core.h |- - - tests/ |- - - - test_first_derivative.cpp |- - - CMakeLists.txt |- - - indicators_core.cpp |- - pybind11/ (Git Submodule) |- src/ |- - DolphinTrading/ |- - - __init__.py |- - - indicator_bindings.cpp |- - - python_indicators.py |- tests/ |- - __init__.py |- - test_first_derivative.py |- - test_moving_average.py |- - utilities.py |- CMakeLists.txt |- LICENSE |- README.md |- MANIFEST.in |- pytest.ini |- setup.py
Примечание. Термин «пакет» несколько неоднозначен. В этом репозитории фактический пакет Python находится по адресу src/DolphinTrading
; пакет Python - это набор модулей Python в каталоге с модулем __init__.py
. Однако обычно весь репозиторий также называют пакетом, поскольку он содержит все файлы, необходимые для сборки и установки пакета Python.
В пакете есть две основные функции, каждая из которых вычисляет технический индикатор: moving_average
и first_derivative
.
Функция moving_average
написана на Python. Он принимает список целых чисел или чисел с плавающей запятой и возвращает список, содержащий простую скользящую среднюю за 3 периода. Исходный код выглядит следующим образом:
Функция first_derivative
написана на C ++. Он принимает вектор с плавающей запятой и возвращает вектор, содержащий соседние различия. Исходный код выглядит следующим образом:
Функция first_derivative
находится в файле с именем indicators_core.cpp
, который мы будем использовать для создания статической библиотеки C ++ с именем indicators_core
.
Для сборки библиотеки мы используем систему сборки CMake; в частности, мы создаем файл CMakeLists.txt в каталоге lib/indicators
со следующим содержимым:
# CMakeLists.txt cmake_minimum_required(VERSION 3.0) project(indicators) SET(CMAKE_CXX_FLAGS "-std=c++0x") add_library(indicators_core STATIC indicators_core.cpp) add_executable(test_first_derivative tests/test_first_derivative.cpp) target_include_directories(indicators_core PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include") target_include_directories(test_first_derivative PUBLIC ../catch2) target_link_libraries(test_first_derivative indicators_core)
Кроме того: если вы ищете отличный ресурс для изучения CMake, посмотрите эту электронную книгу.
Теперь, когда мы создали статическую библиотеку с одной из наших функций технических индикаторов, мы можем создать привязки Python для доступа к ней. Для этого мы создадим файл с именем indicator_bindings.cpp
в src/DolphinTrading
. В этот файл мы включаем библиотеку с именем pybind11
в дополнение к нашей настраиваемой статической библиотеке. Pybind11 - это облегченная библиотека только для заголовков, которая предоставляет типы C ++ в Python и наоборот, в основном для создания привязок Python для существующего кода C ++ [1].
Завершим настройку пакета Python, добавив __init__.py
модуль. Чтобы упростить импорт функций технических индикаторов, добавим в __init__.py
следующее.
# src/DolphinTrading/__init__.py from .indicator_bindings import * from .python_indicators import *
В каталоге верхнего уровня нашего репозитория мы создадим еще один файл CMakeLists.txt. Это создаст общую библиотеку из indicator_bindings.cpp
, которую затем можно будет импортировать в Python, как и любой другой модуль Python. Этот тип модуля называется модулем расширения.
# CMakeLists.txt cmake_minimum_required(VERSION 3.0) project(DolphinTrading) SET(CMAKE_CXX_FLAGS "-std=c++0x") add_subdirectory(lib/indicators) add_subdirectory(lib/pybind11) set(SOURCE_DIR "src/DolphinTrading") set(SOURCES "${SOURCE_DIR}/indicator_bindings.cpp") pybind11_add_module(indicator_bindings ${SOURCES}) target_link_libraries(indicator_bindings PRIVATE indicators_core)
Теперь давайте создадим setup.py
module в каталоге верхнего уровня репозитория. Этот модуль действует как драйвер для создания и установки пакетов Python. Мы будем использовать setuptools, полнофункциональную, активно поддерживаемую и стабильную библиотеку, разработанную для облегчения упаковки проектов Python для настройки нашего пакета.
Чтобы вызвать CMake для создания нашего модуля расширения C ++, мы переопределим классы Extension
и build_ext
из setuptools, как показано ниже:
Мы закончили сборку пакета! В терминале в каталоге верхнего уровня этого репозитория выполните команду: python setup.py install
. Теперь вы можете начать использовать пакет.
Python 3.7.6 (default, Dec 30 2019, 19:38:26) [Clang 11.0.0 (clang-1100.0.33.16)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import DolphinTrading >>> DolphinTrading.first_derivative([2,4,6,9,10]) [2.0, 2.0, 2.0, 3.0, 1.0] >>> DolphinTrading.moving_average([2,3,4,5,6]) [3.0, 4.0, 5.0] >>>
Примечание. Если вы активно разрабатываете пакет, часто рекомендуется установить его через python setup.py develop
. Это позволяет редактировать пакет без необходимости переустанавливать его после каждого изменения. Подробнее см. [2].
Тестирование пакета
Тестирование - неотъемлемая часть процесса разработки программного обеспечения; это особенно верно при разработке пакетов, которые другие пользователи будут интегрировать в свои собственные проекты. Например, можете ли вы представить, что функция квадратного корня Numpy, numpy.sqrt
, содержит ошибку, которая вычисляет кубический корень вместо квадратного корня? Миллионы пользователей полагаются на NumPy и верят, что его функции делают то, что им положено. В этом духе мы создадим простой набор тестов для проверки каждой из функций наших технических индикаторов. Мы будем использовать среду pytest, чтобы писать модульные тесты. Pytest - это простой в использовании, но надежный фреймворк для тестирования. Чтобы установить pytest, выполните команду pip install pytest
.
Примечание. pytest будет рассматривать любую функцию с префиксом test
в модуле test_*.py
или *_test.py
как тестовый пример. Каждый тестовый пример должен иметь оператор assert, который проверяет ожидания тестового примера. См. [3] для получения дополнительной информации.
Наши тесты будут помещены в каталог tests/
. Прежде чем приступить к реализации тестовых примеров, давайте создадим модуль с именем utilities.py
; это будет содержать функции, которые мы будем использовать во всех тестовых примерах. В частности, мы реализуем функцию, которая возвращает True, если два списка равны, и False в противном случае.
Теперь давайте проведем тест индикатора скользящей средней:
И, наконец, что не менее важно, тест на первый производный индикатор:
После завершения тестовых примеров давайте создадим файл с именем pytest.ini
в каталоге верхнего уровня репозитория. Это позволяет нам настроить, как двоичный файл pytest будет запускать наши тесты. В нем мы говорим pytest искать тесты в каталоге tests/
.
[pytest] testpaths = tests
Теперь в терминале, в каталоге верхнего уровня репозитория, выполните команду pytest
. Это запустит все наши тестовые примеры и сгенерирует подробный отчет о том, какие тесты прошли, а какие нет.
Чтобы сделать еще один шаг вперед, мы будем использовать среду тестирования под названием tox в сочетании с pytest; tox создаст изолированные виртуальные среды Python (используя virtualenv), а затем запустит pytest (или другую среду модульного тестирования) в этих средах. Это дает нам возможность моделировать, как наш пакет будет работать в разных средах Python.
Чтобы начать работу с tox, установите его через pip: pip install tox
. Затем добавьте файл tox.ini
на верхний уровень репозитория. В него добавьте следующее:
[tox] envlist = py37 [testenv] deps = pytest commands = pytest
Указанные нами параметры tox.ini дадут указание tox создать виртуальную среду Python с Python 3.7, установить pytest и запустить наш набор тестов с помощью pytest.
Следующие шаги
В следующем руководстве мы узнаем, как создать пакет Python с поддержкой CUDA для вычислительной мощности с ускорением на GPU. Спасибо за прочтение!