Создание модуля расширения для Python на языке Си
Простой, а главное — рабочий пример модуля расширения на языке C для питона. Модуль называется example, а реализовывает одну функцию hello, которая вызывается с параметром who и возвращает «Hello %s» % who.
Листинг модуля example.c
Листинг setup.py
from distutils.core import setup from distutils.extension import Extension examplemodule = Extension(name=»example», sources=[‘example.c’, ]) setup(name=»example», ext_modules=[examplemodule])
python setup.py build
python setup.py install
Выполняем простенький тест:
from example import hello print hello(who=»world!»)
Полезные ссылки (на английском):
Цикл статей про Boost.Python на хабре:
UPD: Для компиляции под Python 3.x вам нужен другой example.c:
Спасибо!
Я бы еще добавил перед «Выполняем простенький тест» раздел «Инсталлируем» с одной строчкой —
[sudo] python setup.py install
И еще интересно как можно собранный у меня на машине пакет инсталлировать на компьютер клиента?
т.е. прокатит ли в скрипте инсталляции моей программы прописать строчку
python setup.py install
чтобы потом использовать данный пакет в своей программе?
Спасибо за дополнение. Обновил запись.
По поводу инсталляции — никогда не возникало такой необходимости, но вот эта команда должна создавать инсталляционный пакет (можно выбрать tgz, zip, rpm и инсталлятор для win32):
python setup.py bdist
А вот здесь можно узнать, как работать с получившимся пакетом: http://docs.python.org/install/index.html
Проверил, работает. Вместо всяког хабраклала.
Рекомендую библиотеку PyCXX для Python функций из C++ и наоборот.
>> import example
Traceback (most recent call last):
File «», line 1, in
а у меня чего-то с такой ошибкой вываливается…
Сложно сказать по такой ошибке что происходит. Вы пишете это в файле или в интерпретаторе?
У меня на FreeBSD все нормально завелось, возможно проблема на вашей стороне.
В первом листинге опечатка нужно PyMODINIT_FUNC вместо PYMODINIT_FUNC
Пишем модуль расширения для Питона на C
OMFG! — может воскликнуть читатель. Зачем писать что-то на С когда есть Python, и будет во многом прав. Однако, к счастьюсожалению наш зелёный друг не всесилен. Итак…
Описание задачи
В рамках текущего проекта (система управления виртуальными машинами, на базе Libvirt), понадобилось программно рулить loop девайсом в Linux. Первая версия когда основанная на вызове командлайн-команды losetup через subprocess.Popen() весьма сносно работала на моей Ubuntu 8.04, однако после деплоя пошли баг-репорты о том что на RHEL и некоторых других системах заявленный функционал не работает. После некоторых разбирательств выяснилось что в них losetup принимает немного другие аргументы, и просто нашу задачу реализовать не получится.
Поковырявшись в исходниках losetup, я увидел что все необходимые мне операции делаются путём отправки IOCTL вызовов в устройство. С питоновским fcntl.ioctl() у меня что-то не заладилось. Было принято решение опуститься на уровень ниже, написать модуль на C.
Disclaimer
Как потом выяснилось fcntl.ioctl() вполне достаточен для реализации всего что мне было нужно. Уже не помню что меня в нём испугало в начале. Наверное нужно работать меньше 10 часов в день 😉
С другой стороны, если бы я сразу его использовал — этого топика бы не было.
Итак ещё раз, для тех кто читает по диагонали — в Питоне есть отличный модуль fcntl.ioctl(). Всё что ниже читать просто как пример.
Планирование API
Всё что можно делать на Питоне — делать на Питоне. То что не получается — выносить в low-level на C.
Того что не получается сделать на питоне — набралось немного: собственно монтирование/размонтирование образа, и проверка, занят ли девайс.
Делаем скелет
Модуль, по аналогии с командлайновой утилитой будет называться losetup. Запускаем любимый Eclipse + PyDev и создаём проект. В нём создаём losetup.py в котором будет весь питоновский код модуля.
Модуль который реализует low-level взаимодействие с системой назовём _losetup. Наш losetup будет импортировать _losetup и использовать его для реализации высокоуровнёвого API.
Создаём папку src, в которой кладём два файла losetupmodule.c и losetupmodule.h
// Исключение которое мы будем бросать в случае какой-то ошибки
static PyObject * LosetupError;
// Монтирование образа в девайс
static PyObject *
losetup_mount (PyObject * self, PyObject * args)
<
return Py_BuildValue( «» );
>
// Размонтирование девайса
static PyObject *
losetup_unmount (PyObject * self, PyObject * args)
<
return Py_BuildValue( «» );
>
// Проверка, смонтировано ли что-то в девайсе
static PyObject *
losetup_is_used (PyObject * self, PyObject * args)
<
int fd, is_used;
const char * device;
struct loop_info64 li;
if ((fd = open (device, O_RDONLY)) 0 ) <
return PyErr_SetFromErrno(LosetupError);
>
is_used = ioctl(fd, LOOP_GET_STATUS64, & li) == 0 ;
// Инициализация
PyMODINIT_FUNC
init_losetup ( void )
<
PyObject * m;
В losetupmodule.h просто набор определений безжалостно выдранный из util-linux-ng
Настраиваем сборку
Собирать модули можно по разному, но самый простой и надёжный — это через setuptools (distutils).
Вся белая магия в строке «ext_modules=[Extension(‘_losetup’, [‘src/losetupmodule.c’], include_dirs=[‘src’])]». Тут описывается расширение с именем _losetup, код которого находится в src/losetupmodule.c, инклуды в src. Этого достаточно чтобы дистутилс мог собрать расширение, установить его, делать из него всяческие пекеджи (в том числе win32 инсталлер, хотя там и не всё так просто).
Проверяем что всё билдится путём вызова «python setup.py build»
Наращиваем мышцы
static PyObject *
losetup_mount (PyObject * self, PyObject * args)
<
int ffd, fd;
int mode = O_RDWR;
struct loop_info64 loopinfo64;
const char * device, * filename;
// Open image file
if ((ffd = open(filename, O_RDWR)) 0 ) <
if (errno == EROFS) // Try to reopen as read-only on EROFS
ffd = open(filename, mode = O_RDONLY);
if (ffd 0 ) <
return PyErr_SetFromErrno(LosetupError);
>
loopinfo64.lo_flags |= LO_FLAGS_READ_ONLY;
>
// Open loopback device
if ((fd = open(device, mode)) 0 ) <
close(ffd);
return PyErr_SetFromErrno(LosetupError);
>
// Set image
if (ioctl(fd, LOOP_SET_FD, ffd) 0 ) <
close(fd);
close(ffd);
return PyErr_SetFromErrno(LosetupError);
>
close (ffd);
Вроде бы несложно, однако возможно не совсем понятно что тут происходит. Давайте разберём основные элементы.
Функции объявленные как METH_VARARGS получают аргументы в виде кортежа. PyArg_ParseTuple() проверяет что аргументы соответствуют указанному шаблону (в данном случае «ss» — две строки), и получает данные, либо, в случае если аргумент не соответствуют шаблону устанавливает ошибку, и возвращает false. Детали о том как это работает можно прочитать в Extracting Parameters in Extension Functions
С точки зрения питона это выглядит так:
Для питона это выглядит так:
Нашей функции не нужно возвращать никаких особых данных, поэтому мы возвращаем None. Подробнее можно прочитать в Building Arbitrary Values
Остальные функции реализуются аналогично.
Публикация на PyPI
Итак модуль написан. Нужно дать человечеству шанс им воспользоваться. Самый простой способ это сделать — опубликовать модуль на Python Package Index.
Регистрируемся на PyPI.
После регистрации пишем в консоли
вводим данные своего аккаунта, и setuptools создёт пакет на PyPI.
делает source destribution (tgz архив с кодом и метаданными), и заливает его на PyPI.
Добавляем losetup как зависимость в setup.py основного приложения. Теперь при его инсталляции setuptools поставит и наш модуль.
Завершение
Вот так, неожиданно легко оказалось опуститься с Python на уровень абстракции ниже, и написать модуль для low-level взаимодействия с системой.
Так-же получили хороший пример того что нужно больше думать и меньше делать. Наш Зелёный Друг могуч, и даже такие экзотические задачи можно решать не расставаясь с ним.
Ускоряем код на Питоне с помощью расширений на Cи
Производительность Си — в программах на Питоне.
Питон — простой, но мощный язык, который заслуженно стал одним из самых популярных. Тем не менее, иногда ему не хватает скорости статически типизированных языков с предварительной компиляцией, таких как Cи и Джава.
Почему Питон — медленный?
Как известно, код на Питоне обычно выполняется интерпретатором, а это часто очень медленный процесс — если сравнивать с Джавой и Си, в которых исходный код компилируется в машинный или байт-код (к сожалению, тема компиляции выходит за рамки статьи).
Как ускорить код на Питоне?
Обычно производительности Питона достаточно — если не приходится выполнять «тяжелые» вычисления, в случае которых как раз и могут пригодиться расширения на Cи.
Расширения — это возможность написать функцию (на Cи), скомпилировать ее в модуль Питона и использовать в исходном коде как обычную библиотеку.
Многие популярные модули написаны на Си или Cи++ (например, numpy, pandas, tensorflow и т. д.) — для повышения производительности и (или) расширения низкоуровневой функциональности.
Расширения на Си работают только на реализации Cpython, но поскольку по умолчанию используется именно она, проблемы в этом быть не должно.
Для применения этого подхода рекомендуется иметь базовые знания Си. Но если вы знаете только Питон, статья тоже будет вполне вам понятна.
Как писать расширения на Си
Прежде всего нам понадобится API для Питона, Python.h — заголовочный файл Си, который содержит всё необходимое для взаимодействия с Питоном.
На Линуксе обычно нужно установить пакет python-dev или python3-dev (если он еще не установлен). (В некоторых дистрибутивах название пакета может отличаться.)
В стандартной установке Windows Питон по умолчанию уже есть.
Прежде чем писать код расширения, необходимо включить пару основных определений и объявлений:
Эти строки рекомендуется поместить в начало файла — для обеспечения совместимости.
В Питоне всё является объектом, поэтому наша функция c_fib(n) тоже должна возвращать объект, а именно указатель PyObject (определенный в Python.h ).
После этого необходимо объявить, какие функции экспортировать из модуля, чтобы они были доступны из Питона.
Определение методов модуля
Каждый экспортированный метод представляет собой структуру, содержащую:
Имя экспортируемого метода (у нас это « c_fib »).
Фактически экспортируемый метод ( c_fib ).
И const char* с описанием метода.
Определение модуля
Функция инициализации модуля
Здесь описаны лишь несколько возможностей API для Питона — подробнее можно почитать на странице документации.
Компиляция расширения в модуль
После написания кода на Си нужно скомпилировать его в модуль для Питона — к счастью, для этого есть множество встроенных инструментов.
Создайте скрипт на Питоне (по традиции — setup.py ) со следующим кодом:
В командной строке выполните следующее:
python3 setup.py build
python3 setup.py install
В систему будут установлены только что собранные библиотеки, и ими можно будет пользоваться откуда угодно.
Для этой команды могут понадобиться права администратора или суперпользователя (root). Можно не выполнять установку для всей системы, но в этом случае для использования расширения придется задействовать относительный импорт.
Использование расширения в программе на Питоне
В файле Питона импортируйте только что созданный модуль, используя выбранное имя — в нашем случае это c_module :
Как видите, расширение используется так же, как и любой другой модуль.
Сравнение с версией на «чистом» Питоне
Теперь сравним функцию c_fib с ее аналогом на Питоне. Воспользуемся встроенным модулем time :
Как и ожидалось, функция на Си работает быстрее.
На различных компьютерах время исполнения будет различаться, но версия на Си всегда будет быстрее.
А теперь попробуем на больших числах:
Версия Си явно превосходит версию на Питоне в случае больших чисел. Если нужно выполнить несколько простых вычислений, то использование Си вряд ли будет оправданно, поскольку разница в производительности будет минимальной. Но если у вас трудоемкая операция или функция, которую необходимо выполнять много раз, скорости Питона может быть недостаточно.
И здесь расширения на Си могут здорово выручить: так вы поручите всю тяжелую работу производительному языку, а в качестве основного продолжите использовать Питон.
Примеры использования
Допустим, есть задача выполнить трудоемкие вычисления — например, для криптографического алгоритма, глубокого машинного обучения или обработки больших объемов данных. В этом случае расширения на Си могут снять нагрузку с интерпретатора Питона и ускорить работу приложения.
Что, если вам нужно создать низкоуровневый интерфейс или работать с памятью непосредственно из Питона? Здесь тоже стоит использовать расширения на Си — если вы знаете, как работать с «чистыми» указателями.
Еще один практический пример — оптимизация уже существующего «подтормаживающего» приложения на Питоне без переписывания его на другом языке.
А возможно, вы просто обожаете оптимизацию и хотите, чтобы код работал как можно быстрее, но не спешите расставаться с высокоуровневыми абстракциями для работы с сетью, графическим интерфейсом и т. д. — тогда вы определенно полюбите расширения на Си.
Время — ресурс, которого всегда не хватает. Используйте его с умом.
Заключение
Расширения на Си — отличное дополнение в арсенале разработчика, будь вы фанат производительности и эффективности или любитель смешивать различные технологии и экспериментировать с чем-то новым: вы не только получаете почти «бесплатный» скачок производительности, но и расширяете функциональные возможности Питона, не прибегая к устаревшему стеку технологий.
Благодарю за внимание.
О переводчике
Перевод статьи выполнен в Alconost.
Alconost занимается локализацией игр, приложений и сайтов на 70 языков. Переводчики-носители языка, лингвистическое тестирование, облачная платформа с API, непрерывная локализация, менеджеры проектов 24/7, любые форматы строковых ресурсов.
Мы также делаем рекламные и обучающие видеоролики — для сайтов, продающие, имиджевые, рекламные, обучающие, тизеры, эксплейнеры, трейлеры для Google Play и App Store.
Расширение Python с помощью C
Есть несколько способов ускорения кода Python:
Поиск простых чисел
Нам нужно вычислить простое число k. Ниже представлен один из способов выполнить это в Python:
Этому коду требуется около 42 секунд для вычисления 10000-го простого числа. Посмотрим, насколько быстро C сможет выполнить эту задачу с тем же алгоритмом:
extern int kthPrime(int n);
Выполнение заняло всего 1,01 секунды!
Таким образом, если бы Python использовал этот модуль для вычисления простого числа k, мы бы сэкономили много времени. К счастью, это вполне осуществимо. Python предоставляет множество API для расширения функциональных возможностей.
Для начала создадим простую библиотеку Python с C для вычисления простого числа k. Выполним следующие шаги:
Интеграция
Создаем функциональность для реализации CPython — структуры под названием PyObject. На этом этапе нам необходимо преобразовать типы данных C для использования в Python. В данном случае мы конвертируем все элементы в PyObjects.
Приведенный выше код C может показаться немного сложным тем, кто не знаком с такими сильно типизированными языками, как C++ или Java, но на самом деле он довольно прост.
Как было сказано ранее, все элементы представлены в PyObject. Статическая функция, которая возвращает указатель на PyObject, вызывается CPython при импорте библиотеки и вызове функции kthprime. Эта функция принимает два аргумента: self и args. Первая ссылается на PyObject, который вызвал функцию, или на сам экземпляр модуля.
Python также предоставляет множество методов C для анализа аргументов. PyArg_ParseTuple является одним из них.
Поскольку аргумент k передан как позиционный аргумент, Python передает его в качестве кортежа с одним элементом. «i» сообщает PyArg_ParseTuple о том, что необходимо найти целое число.
Если на каком-либо этапе статического метода вы возвращаете указатель NULL, интерпретатор Python предполагает, что что-то пошло не так, и выдает исключение. Если функция ничего не возвращает, передайте объект Py_None.
Сборка
Настало время собрать все части воедино.
Для начала компилируем код c_prime.c в перемещаемый формат. С помощью GCC можно скомпилировать код C в объектный файл, который будет содержать код в файле c_prime.c в формате машинного кода.
Затем компилируем код fastprime.c в объектный код, как было сказано ранее. Теперь соединяем все объектные файлы вместе со стандартной библиотекой для создания финальной версии кода. К счастью, Python предоставляет библиотеку disutils, которая значительно упрощает этот процесс.
Этот фрагмент кода также выполняется за 1,01 секунды!
Таким образом, нам удалось ускорить код примерно в 40 раз. Возникает вопрос: почему этот способ так редко используется? На это есть несколько причин.
Во-первых, этот процесс занимает много времени. Если производительность важна для вашей системы, то, вероятно, вам изначально не следует использовать Python. Главная особенность Python заключается в том, что большая часть времени уходит на чтение, а не написание кода. Улучшенная читабельность кода помогает ему дольше сохранять свою актуальность.
Во-вторых, за ускорение приходится расплачиваться. Первоначальный медленный код Python был независим от машины. Как известно, Python компилируется в байт-код, а затем интерпретируется на виртуальной машине Python. Таким образом, можно сразу приступать к распространению кода, а всю оставшуюся часть работы предоставить Python (при использовании совместимой версии Python для запуска кода). А вот генерируемые объекты файлы C и C++ зависят от целевой машины. Таким образом, при внедрении объектных файлов на C код Python становится машинно-зависимым.
Создание расширения C++ для Python
Модули, написанные на C++ (или C), обычно используются для расширения возможностей интерпретатора Python. Они также используются для обеспечения доступа к низкоуровневым возможностям операционной системы.
Модули можно разделить на три следующих основных типа.
В этой статье также показаны два способа сделать расширение C++ доступным в Python:
Полный пример из этого руководства см. на GitHub по ссылке python-samples-vs-cpp-extension.
Предварительные требования
Visual Studio 2017 или более поздней версии с установленной рабочей нагрузкой разработки на Python. Эта рабочая нагрузка включает встроенные средства разработки Python, которые подключают рабочую нагрузку C++ и наборы инструментов, необходимые для собственных расширений.
При установке рабочей нагрузки Приложения для обработки и анализа данных и аналитические приложения среда Python и встроенные средства разработки Python устанавливаются по умолчанию.
Дополнительные сведения о параметрах установки см. в разделе Установка поддержки Python в Visual Studio. Если вы устанавливаете Python отдельно, обязательно выберите параметр Скачать отладочные символы в разделе Дополнительные параметры установщика. Этот параметр необходим, чтобы вы могли использовать отладку в смешанном режиме между кодом Python и машинным кодом.
Создание приложения Python
Создайте проект Python в Visual Studio, выбрав Файл > Создать > Проект. Выполните поиск по ключевому слову Python, выберите шаблон Приложение Python, задайте имя и расположение, а затем нажмите кнопку OK.
В файл проекта с расширением .py вставьте следующий код. Чтобы испытать некоторые функции редактирования Python, попробуйте ввести этот код вручную.
Этот код вычислит гиперболический тангенс без использования математической библиотеки, что позволит вам ускорить работу встроенных расширений.
Сначала напишите код только на Python, а затем перепишите его на C++. Таким образом вам будет проще проверить правильность машинного кода.
Чтобы просмотреть результаты, запустите программу, выбрав в меню Отладка > Запуск без отладки или нажав клавиши CTRL + F5.
При запуске тестов производительности всегда выбирайте в меню Отладка > Запуск без отладки. Это помогает избежать дополнительных временных затрат, возникающих при запуске кода в отладчике Visual Studio.
Создание основных проектов C++
Следуйте инструкциям в этом разделе, чтобы создать два идентичных проекта C++ с именами superfastcode и superfastcode2. Позднее вы будете использовать в этих проектах разные способы предоставления кода C++ в Python.
В обозревателе решений щелкните решение правой кнопкой мыши и последовательно выберите пункты Добавить > Новый проект. Решение Visual Studio может одновременно содержать проекты Python и C++ — это одно из преимуществ использования Visual Studio для Python.
Или, если вы установили собственные средства разработки Python в Visual Studio, можете начать с шаблона «Модуль расширения Python». В этом шаблоне уже имеется большая часть описанных здесь возможностей.
Но в этом пошаговом руководстве начало работы с пустого проекта позволяет шаг за шагом продемонстрировать создание модуля расширения. Ознакомившись с процессом, в дальнейшем вы сможете использовать этот шаблон для экономии времени при написании собственных расширений.
Чтобы создать файл C++ в новом проекте, щелкните правой кнопкой мыши узел Исходные файлы и выберите Добавить > Новый элемент.
Файл с расширением .cpp нужен, чтобы активировать страницы свойств C++ в последующих шагах.
На главной панели инструментов используйте раскрывающееся меню, чтобы выполнить одно из следующих действий.
В Обозревателе решений щелкните правой кнопкой мыши проект C++, выберите пункт Свойства, а затем выполните следующие действия.
При создании собственных проектов вам понадобится настроить конфигурации и отладки, и выпуска. В этом уроке вы настроите только конфигурацию отладки и установите в ней использование сборки выпуска CPython. Эта конфигурация отключает некоторые функции отладки среды выполнения C++, в том числе утверждения. Для использования двоичных файлов отладки CPython (python_d.exe) требуются другие параметры.
Задайте свойства, как показано в следующей таблице.
Если вы не видите вкладку C/C++ в свойствах проекта, значит, в проекте нет файлов, определенных как исходные файлы C/C++. Такая ситуация может возникнуть, если вы создали исходный файл без расширения .c или .cpp.
Например, если в диалоговом окне создания элемента вы случайно введете module.coo вместо module.cpp, Visual Studio создаст файл, но не задаст для него тип Код C/C, что требуется для активации вкладки свойств C/C++. Такая неправильная идентификация сохранится, даже если вы переименуете файл и добавите расширение .cpp.
Щелкните ОК.
Чтобы протестировать конфигурации (как для отладки, так и для выпуска), щелкните правой кнопкой мыши проект C++ и выберите пункт Сборка.
Обратите внимание, что файлы .pyd находятся в папке solution в подпапках Debug и Release, а не в самой папке проекта C++.
Добавьте следующий код в файл module.cpp проекта С++.
Еще раз выполните сборку проекта C++, чтобы убедиться в правильности кода.
Если вы еще этого не сделали, повторите предыдущие действия, чтобы создать второй проект с именем superfastcode2 и тем же содержимым.
Преобразование проекта C++ в расширения для Python
Чтобы превратить библиотеку DLL C++ в расширение для Python, сначала нужно изменить экспортированные методы для взаимодействия с типами Python. После этого нужно добавить функцию, экспортирующую модуль, а также определения методов модуля.
В последующих разделах объясняется, как выполнить эти действия с помощью расширений CPython и PyBind11.
Использование расширений CPython
Дополнительные сведения о коде, приведенном в этом разделе, см. в справочном руководстве по API Python/C, в частности на странице Объекты модуля. Не забудьте выбрать версию Python в раскрывающемся списке в правом верхнем углу.
В верхней части файла module.cpp включите Python.h.
Добавьте структуру, определяющую способ представления функции tanh_impl C++ для Python:
Имя, которое импортируется в этом коде, должно соответствовать значению в свойствах проекта в разделе Свойства конфигурации > Общее > Имя целевого объекта.
Еще раз выполните сборку проекта C++, чтобы проверить код. Если возникли ошибки, см. раздел Устранение неполадок.
Использование PyBind11
Если вы выполнили действия, описанные в предыдущем разделе, вы наверняка заметили, что использовали много стандартного кода для создания необходимых структур модуля для кода C++. PyBind11 упрощает процесс с помощью макросов в файле заголовка C++, которые позволяют получить тот же результат с гораздо меньшим объемом кода.
Дополнительные сведения о коде в этом разделе см. в разделе Основы PyBind11.
Вы также можете установить PyBind11 с помощью окна «Окружения Python», а затем использовать его команду Открыть в PowerShell для выполнения следующего шага.
В верхнюю часть нового файла module.cpp, который не содержит изменения из предыдущего раздела, включите pybind11.h.
В нижней части module.cpp с помощью макроса PYBIND11_MODULE определите точку входа в функцию C++.
Выполните сборку проекта C++, чтобы проверить код. При возникновении ошибок см. способы их устранения в следующем разделе «Устранение ошибок компиляции».
Устранение ошибок компиляции
Модуль C++ может не компилироваться по следующим причинам.
Ошибка: не удается обнаружить Python.h (E1696: не удается открыть исходный файл Python.h и/или C1083: не удается открыть включаемый файл: Python.h — не существует такого файла или каталога)
Решение: убедитесь, что путь в разделе C/C++ > Общие > Дополнительные каталоги включаемых файлов в свойствах проекта указывает на папку include установки Python. См. шаг 6 в разделе Создание основного проекта C++.
Ошибка: не удается обнаружить библиотеки Python
Решение: убедитесь, что путь Компоновщик > Общие > Дополнительные каталоги библиотек в свойствах проекта указывает на папку libs установки Python. См. шаг 6 в разделе Создание основного проекта C++.
Ошибки компоновщика, связанные с целевой архитектурой
Решение: измените целевую архитектуру проекта C++ в соответствии с вашей установкой Python. Например, если вы хотите использовать в проекте C++ версию Win32, но у вас установлена версия Python x64, измените проект C++ для работы с версией х64.
Тестирование кода и сравнение результатов
Теперь, когда библиотека DLL структурирована как расширения Python, можно ссылаться на них из проекта Python, импортировать модули и использовать их методы.
Предоставление доступа к библиотеке DLL для Python
Библиотеку DLL можно сделать доступной для Python несколькими способами. Покажем два метода, которые рекомендуется рассмотреть.
Первый метод работает, если проект Python и проект C++ находятся в одном решении. Выполните следующие действия:
В Обозревателе решений щелкните правой кнопкой мыши узел Ссылки в вашем проекте Python, а затем выберите команду Добавить ссылку.
В открывшемся диалоговом окне перейдите на вкладку Проекты, выберите проекты superfastcode и superfastcode2 и нажмите кнопку OK.
В другом методе устанавливается модуль в вашей среде Python, что делает этот модуль доступным и для других проектов Python. Дополнительные сведения см. в документации по проекту setuptools. Выполните следующие действия:
Создайте в проекте C++ файл с именем setup.py, щелкнув проект правой кнопкой мыши и выбрав пункт Добавить > Новый элемент.
Если файлу назначить имя с расширением .py, это позволяет Visual Studio распознать его как файл Python, несмотря на использование шаблона файла C++.
Когда файл откроется в редакторе, вставьте в него следующий код в зависимости от метода расширения.
Для расширений CPython (проект superfastcode)
Для PyBind11 (проект superfastcode2)
В проекте C++ создайте второй файл с именем pyproject.toml и вставьте в него следующий код.
Чтобы создать расширение, щелкните правой кнопкой мыши открытую вкладку pyproject.toml и выберите Копировать полный путь. Вы удалите имя pyproject.toml из пути, прежде чем использовать его.
В Обозревателе решений щелкните правой кнопкой мыши активную среду Python и выберите пункт Управление пакетами Python.
Если пакет уже установлен, он появится в списке. Прежде чем продолжить, щелкните значок X, чтобы удалить его.
В поле поиска вставьте скопированный путь, удалите pyproject.toml в конце пути и нажмите клавишу ВВОД, чтобы установить модуль из этого каталога.
Если установка завершается неудачно из-за ошибки, связанной с разрешениями, добавьте в конец —user и попробуйте выполнить команду еще раз.
Вызов библиотеки DLL из Python
После того как вы сделаете библиотеки DLL доступными для Python, как описано в предыдущем разделе, можно вызывать функции superfastcode.fast_tanh и superfastcode2.fast_tanh2 из кода Python и сравнивать их эффективность с реализацией Python. Чтобы вызвать библиотеку DLL, выполните следующие действия.
Добавьте следующие строки в файл .py, чтобы вызвать методы, экспортированные из библиотек DLL, и отобразить их выходные данные.
Запустите программу Python, выбрав в меню Отладка > Запуск без отладки или нажав клавиши CTRL + F5.
Если команда Запуск без отладки отключена, в Обозревателе решений щелкните правой кнопкой мыши проект Python и выберите команду Назначить запускаемым проектом.
Убедитесь, что подпрограммы C++ выполняются примерно в 5–20 раз быстрее, чем реализация Python. Обычно выводится следующий результат.
Кроме того, отладочная сборка модуля C++ выполняется медленнее, чем сборка выпуска, так как отладочная сборка менее оптимизирована и содержит разные проверки ошибок. Вы можете переключаться между этими конфигурациями для сравнения, но не забудьте вернуться и обновить заданные ранее свойства для конфигурации выпуска.
Чтобы сократить временные затраты, можно переместить цикл for в машинный код. Этот подход предполагает использование протокола итератора (или типа py::iterable PyBind11 для параметра функции) для обработки каждого элемента. Удаление повторяющихся переходов между Python и C++ является эффективным способом сокращения времени, затрачиваемого на обработку последовательности.
Устранение ошибок импорта
При сборке с помощью ссылки на проект убедитесь, что свойства проекта C++ соответствуют среде Python, активированной для проекта Python, особенно каталоги Include и Library.
Убедитесь, что ваш выходной файл имеет имя superfastcode.pyd. Файл с любым другим именем или расширением не будет импортирован.
Если вы установили модуль с помощью файла setup.py, убедитесь, что вы выполнили команду pip в среде Python, активированной для вашего проекта Python. При развертывании среды Python в обозревателе решений должна отображаться запись для superfastcode.
Отладка кода C++
Visual Studio поддерживает совместную отладку кода на Python и C++. В этом разделе вы пошагово выполните процесс с использованием проекта superfastcode. Этот процесс такой же, как для проекта superfastcode2.
В Обозревателе решений щелкните проект Python правой кнопкой мыши, выберите Свойства, откройте вкладку Отладка и выберите Отладка > Разрешить отладку машинного кода.
При включении отладки машинного кода окно вывода Python может немедленно закрываться сразу после завершения программы без обычной паузы с сообщением Для продолжения нажмите любую клавишу.
Чтобы сохранить изменения свойств, выберите пункт меню Файл > Сохранить.
В панели инструментов Visual Studio установите для конфигурации сборки режим Отладка.
Так как в отладчике код обычно выполняется дольше, вы, возможно, захотите уменьшить значение переменной COUNT в файле .py приблизительно в пять раз. Например, вместо 500000 задать 100000.
Когда выполнение доходит до кода точки останова, отладчик останавливается. Если точка останова не сработает, проверьте, выбрана ли конфигурация отладки и сохранен ли проект (при запуске отладчика он не сохраняется автоматически).
В точке останова вы можете пошагово проходить по коду C++, проверять переменные и т. д. Дополнительные сведения об этих функциях см. в разделе Одновременная отладка Python и C++.
Альтернативные подходы
Метод | Vintage | Представители |
---|---|---|
Модули расширений C/C++ для CPython | 1991 | Стандартная библиотека |
PyBind11 (рекомендуется для C++) | 2015 | |
Cython (рекомендуется для C) | 2007 г. | gevent, kivy |
HPy | 2019 | |
mypyc | 2017 | |
ctypes | 2003 | oscrypto |
cffi | 2013 | cryptography, pypy |
SWIG | 1996 | crfsuite |
Boost.Python | 2002 | |
cppyy | 2017 |
См. также
Полный пример из этого руководства см. на GitHub по ссылке python-samples-vs-cpp-extension.