Интеграция Python и C++
Недавно при прототипировании одной из частей разрабатываемого нами продукта возникла одна интересная задача: нужно было проверить склейку Python и C++. Связано это было с тем, что основной код был написан на плюсах, и необходимо было подключить внешнюю библиотеку Websockets, написанную на Python (на тот момент не было соответствующей библиотеки на C++). Схема взаимодействия при такой задаче достаточно простая. Из C++ вызывается функция подключения к серверу (на python), в качестве параметра передается его адрес. Соответственно, при получении сообщния Python передавает его обратно в метод C++.
При написании кода использовалась питоновская библиотека Websocket от Autobahn (http://www.tavendo.de/autobahn/clientlibraries.html), которую было необходимо вызывать из C++. Для этих целей в Python предусмотрен Python C-API (http://docs.python.org/extending/index.html), однако многие простые действия, например, вызов функций делается несколькими действиями. После небольшого гугления был найден ряд библиотек, позволяющих упростить подобные действия: Boost.Python (http://www.boost.org/doc/libs/1_39_0/libs/python/doc/index.html), SWIG(http://www.swig.org/), Py++, Pybindgen, Pyrex… В результате был выбран Boost.Python как наиболее популярное решение.
Для начала напишем простенький эхо клиент на Python, который будет раз в секунду посылать сам себе сообщение “Hello world”, принимать его и отдавать в C++. cppMethods будет объявлен в C++ коде, cppMethods.printMessage(msg) — как раз место склейки со стороны Python, непосредственно вызов функции C++, которая будет печатать полученное сообщение.
Вот код Python — echo-client.py:
from twisted.internet import reactor from autobahn.websocket import WebSocketClientFactory, WebSocketClientProtocol, connectWS import cppMethods class EchoClientProtocol(WebSocketClientProtocol): def sendHello(self): self.sendMessage("Hello, world!") def onOpen(self): self.sendHello() def onMessage(self, msg, binary): cppMethods.printMessage(msg) reactor.callLater(1, self.sendHello) def Connect(addressStr): factory = WebSocketClientFactory(addressStr) factory.protocol = EchoClientProtocol connectWS(factory) reactor.run()
Теперь напишем на C++ код, в котором опишем нашу функцию, вызываемую из питона. Для использования Python C-API нужно проинклудить Python.h. Обратите внимание, что на этом этапе мы еще не используем Boost.Python, лишь собственно родной Python C-API.
#include #include #include static PyObject * printString(PyObject * self, PyObject* args) < const char * toPrint; if(!PyArg_ParseTuple(args, "s", &toPrint)) < return NULL; >std::cout static PyMethodDef EmbMethods[] = < , >;
В последнем объявлении мы описали, что при вызове функции printMessage из Python будет вызван C++ метод printString.
Ну и наконец свяжем все это вместе. Для проверки работы websockets, помимо эхо-клиента, была использована ссылка на html5labs.
WebSocketConnect.cpp:
#include #include #include #include #include "PrintEmb.cpp" void WebSocketConnect() < using namespace boost::python; Py_Initialize(); Py_InitModule("cppMethods", EmbMethods); PyObject * ws = PyImport_ImportModule("echo_client"); std::string address = "ws://html5labs-interop.cloudapp.net:4502/chat"; call_method(ws, "Connect", address); Py_Finalize(); >
В этом месте мы все-таки воспользовались возможностями Boost, а именно функцией call_method, иначе бы нам понадобилось написать существенно больше кода.
Ну вот как-то так. Здесь мы поинициализировали EmbMethods для питона, назвав их cppMethods, а затем вызвали из Python метод Connect и передали в него строку “address”. В результате наше приложение раз в секунду печатает строку «Hello World» (которую посылает сам себе питон), а также любое сообщение, приходящее с сервера вебсокетов.
Вот таким образом можно связать Python и C++. Буду благодарен за комментарии по теме.
Upd. Поправил код в соответствии с замечаниями из комментариев.
Как можно синхронизировать C++ и Python
Всем привет! Столкнулся с такой необходимостью, у меня есть код, написанный на C++ это программа со статистикой(там диаграмма и графики). И есть код на Python, который использует нейросеть для определения эмоций(tensorflow и opencv). Можно ли что-то сделать чтобы они работали вместе, то есть python работал в фоне и экспортировал статистику в C++. Я бы смог написать это все вместе. Но не получается найти скомпилированную библиотеку tensorflow для C++(для её компиляции требуется 12 ГБ ОЗУ, у меня только 8).
Отслеживать
задан 14 мая 2018 в 18:06
129 7 7 бронзовых знаков
Посмотрите, например, на передачу данных по пайпу
14 мая 2018 в 18:29
@avp, а как это можно реализовать? Если есть возможность скиньте какой-нибудь гайд на русском. В гугле информации про это мало. Я в python’е не силен.
14 мая 2018 в 18:33
Гайдов не помню. Поищите примеры с fork/dup/exec, в которых stdin/stdout нового процесса переключаются на пару пайпов (man pipe) Т.о. python запускаете в дочернем процессе, он читает stdin и пишет ответы в stdout
14 мая 2018 в 18:39
существует множество способов связать питон и c++ код (помимо обычных IPC методов)¶ Мы живём в интересные времена, если люди могут tensorflow использовать, но не знают как связать две команды с помощью pipe¶ Если C++ заметную работу вычислительную выполняет, то из Питона можно вызовы делать, если нет, то графопостроительством может быть удобней из Питона заниматься. Как с помощью python сделать модуль для c++
14 мая 2018 в 20:24
@avp Это не будет компилироваться на винде же. Cygwin такое вроде и умеет, но это частный случай. Поэтому лучше уже юзать сокеты, ибо для сокетов можно найти какую-нибудь кроссплатформенную либу
Python-сообщество
![]()
- Начало
- » Python для новичков
- » Связать питон и С++
#1 Июль 5, 2011 23:15:04
Обедающий философ От: Зарегистрирован: 2011-05-06 Сообщения: 66 Репутация: 0 Профиль Отправить e-mail
Связать питон и С++
Написал я программу на питоне, а она работает в сотни раз медленнее, нежели могла бы. Посему я решил переписать её на С++. Но хотелось бы иметь к ней доступ из питона, ибо, во-первых, переписывать ГУИ совершенно ни к чему, во-вторых, из питона удобнее отлаживать. Посему вопрос — через что организовать интерфейс питон-С++? Я почитал официальное руководство, там, насколько я понял, предлагают каждый раз пересобирать питон (!) с моей библиотекой. А на другом сайте есть статья, в которой характеризуются ажно десять различных методов достижения означенной цели. Очевидно, что я не могу сколько-либо адекватно сделать выбор, ибо являюсь новичком как в питоне, так и в С++. Посему я прошу вас сделать этот выбор за меня.
Грубо говоря, программа-минимум — вызывать из питона функцию, отвечающую за расчёт, и принимать несколько двумерных массивов чисел для отрисовки. Программа-максимум — иметь доступ ко всем моим С++ным функциям и объектам из питона, дабы отлаживать в интерактивном режиме.
Система — линукс (ubuntu), возможно, придётся адаптировать под windows.
#2 Июль 5, 2011 23:34:09
doza_and От: Зарегистрирован: 2010-08-15 Сообщения: 4138 Репутация: 252 Профиль Отправить e-mail
Связать питон и С++
для “с” посмотрите модуль ctypes Для “c++” boost python
#3 Июль 9, 2011 10:04:45
alex312 От: Зарегистрирован: 2009-11-06 Сообщения: 6 Репутация: 0 Профиль Отправить e-mail
Связать питон и С++
#4 Июль 9, 2011 20:25:19
Обедающий философ От: Зарегистрирован: 2011-05-06 Сообщения: 66 Репутация: 0 Профиль Отправить e-mail
Связать питон и С++
doza_and
Для “c++” boost python
Посмотрел, там вроде как надо собирать с помощью build.boost, а я обычный make с грехом пополам асилил. Вопщем, я сделал вывод, что пока я не готов это дело осваивать, лучше всё на голом С++ напишу. Уже сделал подобие графики через ncurses.
alex312
Попробуйте Numpy — http://numpy.scipy.org/
Ежели я ничего не путаю, то я numpy собственно использовал. Матрицы умножает на ура, а во всём остальном совершенно не помогает.
#5 Июль 9, 2011 21:36:33
doza_and От: Зарегистрирован: 2010-08-15 Сообщения: 4138 Репутация: 252 Профиль Отправить e-mail
Связать питон и С++
Обедающий философ
в сотни раз медленнее
Обычно бывает 3 ну 5 раз медленнее, иногда вовсе не медленнее чем c++. Если в сотни раз — есть непонимание того что делается
— его надо устранять.
Обедающий философ
я не готов это дело осваивать, лучше всё на голом С++ напишу
c++ посложнее питона будет (намного). Хотя конечно c++ + python наверное сложнее каждой из частей.
Вы конкретнее задачу опишите — может кто что и присоветует. ЕСть много способов ускоритть питон и отлаживать в c++
Отредактировано (Июль 9, 2011 21:37:47)
#6 Июль 9, 2011 23:09:55
Обедающий философ От: Зарегистрирован: 2011-05-06 Сообщения: 66 Репутация: 0 Профиль Отправить e-mail
Связать питон и С++
doza_and
Обычно бывает 3 ну 5 раз медленнее, иногда вовсе не медленнее чем c++. Если в сотни раз — есть непонимание того что делается
— его надо устранять.
Вот я как раз сегодня до сколько-либо работоспособного состояния довёл — получилось в несколько десятков раз быстрее.
doza_and
Вы конкретнее задачу опишите — может кто что и присоветует.
Ну скажем так, есть прямоугольная сетка, на ней некоторое множество модельных организмов (т. н. агентов). Внутри каждого агента нейронная сеть (собственно в ней и происходит умножение матрицы на вектор, матрица размером около 10*10). И каждый агент может передвигаться из клетки в клетку, общаться с другими агентами, размножаться (попутно мутируя) и умирать. То бишь получается навроде генетического алгоритма (такие вещи ещё называют “искусственной жизнью”, хотя данная конкретная модель не сказать чтобы уж очень живая).
Стало быть, на питоне умножение матриц занимает какую-то пренебрежимо малую часть времени, а основное — это вычисление сигмовидной функции (необходимой для нейронной сети, 10 штук на агента на шаг) и “органов чувств” агента, которые представляют собой банальное копирование (ну в крайнем случае вычисление логарифма). Согласитесь, это смешно. Тогда как на С++, напротив, умножение матриц занимает львиную долю времени. Правда, я ещё не сделал размножение, общение и мутацию, но по идее они отнимают не очень много машинного времени.
Бугага, оказывается, коли сигмовидную функцию определять как 1/(1+exp(-a)), то суммарное время выполнения вырастает в два раза по сравнению с отсутствием сигмы. А ежели её определить как 1/(1+exp(-a*0.001)), то время выполнения почти сравнивается с временем без сигмы. А ежели сигму определять как арктангенс, то ещё лучше. Я так подозреваю, что ей не нравится, что экспонента принимает чересчур большое значение, и что на это значение ещё надо поделить.
#7 Июль 9, 2011 23:26:52
doza_and От: Зарегистрирован: 2010-08-15 Сообщения: 4138 Репутация: 252 Профиль Отправить e-mail
Связать питон и С++
Да, думаю Агентов тут надо сажать в c++ вместе с сеткой в которой они живут. В этой задаче в питоновскую часть наверное стоит вынести сбор статистики и расчет функционалов (они ведь наверное нужны?) Они достаточно часто меняются. Кроме того можно туда отправить и графическое отображение состояния. Чистый ctypes будет тут не очень удобен — для работы скорее всего потребуется доступ к классам.
тут swig или boost наиболее частые решения. bjam не очень страшный зверь. Вам нужно только цеплять питон тогда — действуете по инструкции и все изучать премудрости jam не требуется. Освоение будет быстрее чем освоить make. Если будут проблемы спрашивайте.
Питон очень сильно помогает не столько для отладки сколько для анализа результатов — когда надо посмотреть то одно то другое.
Отредактировано (Июль 9, 2011 23:28:51)
#8 Июль 10, 2011 12:15:02
Обедающий философ От: Зарегистрирован: 2011-05-06 Сообщения: 66 Репутация: 0 Профиль Отправить e-mail
Связать питон и С++
Ну по идее можно на С++ статистики в лог писать, а на питоне читать файл и строить графики, таким образом непосредственная связка не нужна. Кстати, можно ли это делать в реалтайме, то бишь запускать из питона программу на С++ и читать питоном файл по мере его наполнения?
Непосредственную связку С++ и питона, видимо, всё же изучу попозже, ежели появится необходимость. Спасибо за помощь!
#9 Июль 10, 2011 16:27:08
doza_and От: Зарегистрирован: 2010-08-15 Сообщения: 4138 Репутация: 252 Профиль Отправить e-mail
Связать питон и С++
doza_and
можно на С++ статистики в лог писать
Это мы уже двадцать раз проходили. Все зависит от масштаба. Если ваши желания это сделать чтото типа программы wc (word count) на этом можно остановиться.
но если проект более или менее большой — 90% вашего кода будет read write, и я многих таких людей знаю, им просто некогда программить все read-write,read-write…. Судя по вашему описанию — у вас что-то посложнее wc.
Один раз освоив технологию (за 1 день) вы существенно сократите расходы на написание интерфейсов я бы крайне не рекомендовал лениться — потом за это 10 раз заплатите. Связаться с сишным процессом тоже можно см subprocess.Popen, sockets. Тут вы или не получите интерактивности (будете выполнять процесс до конца и не сможете продолжить работу) или вынуждены будете осваивать тонкости синхронизации процессов (думаю дня за 2 легко освоить) Кстати помоему самый простой способ общения со стороны c++ это опять-же освоить boost и использовать библиотеку asio.
Отредактировано (Июль 10, 2011 16:29:03)
Создание расширения C++ для Python
Область применения:
Visual Studio Visual Studio для Mac
Visual Studio Code ![]()
Модули, написанные на C++ (или C), обычно используются для расширения возможностей интерпретатора Python. Они также обеспечивают доступ к низкоуровневым возможностям операционной системы.
Модули можно разделить на три следующих основных типа.
- Модули ускорения. Так как Python является интерпретируемым языком, вы можете писать модули ускорителя на C++ для повышения производительности.
- Модули-оболочки. Эти модули открывают существующие интерфейсы C/C++ для кода Python или предоставляют адаптированный API, который удобно использовать в Python.
- Модули низкоуровневого системного доступа. Эти модули создаются для доступа к низкоуровневым функциям среды выполнения CPython , операционной системы или базового оборудования.
В этой статье рассматривается создание модуля расширения C++ для CPython , который вычисляет гиперболический тангенс и вызывает его из кода Python. Подпрограмма реализована сначала на языке Python, чтобы продемонстрировать относительный прирост производительности по сравнению с реализацией той же подпрограммы на C++.
В этой статье также показаны два способа сделать расширение C++ доступным в Python:
- использование стандартных расширений CPython , как описано в документации по Python;
- использование PyBind11 (рекомендуется для C++ 11 благодаря его простоте). Для обеспечения совместимости используйте какую-то из более актуальных версий 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++. Таким образом вам будет проще проверить правильность машинного кода.
from random import random from time import perf_counter COUNT = 500000 # Change this value depending on the speed of your computer DATA = [(random() - 0.5) * 3 for _ in range(COUNT)] e = 2.7182818284590452353602874713527 def sinh(x): return (1 - (e ** (-2 * x))) / (2 * (e ** -x)) def cosh(x): return (1 + (e ** (-2 * x))) / (2 * (e ** -x)) def tanh(x): tanh_x = sinh(x) / cosh(x) return tanh_x def test(fn, name): start = perf_counter() result = fn(DATA) duration = perf_counter() - start print('<> took seconds\n\n'.format(name, duration)) for d in result: assert -1 '.format(COUNT)) test(lambda d: [tanh(x) for x in d], '[tanh(x) for x in d] (Python implementation)')
Совет При запуске тестов производительности всегда выбирайте в меню Отладка>Запуск без отладки. Это помогает избежать дополнительных временных затрат, возникающих при запуске кода в отладчике Visual Studio.
Создание основных проектов C++
Следуйте инструкциям в этом разделе, чтобы создать два идентичных проекта C++ с именами superfastcode и superfastcode2. Позднее вы будете использовать в этих проектах разные способы предоставления кода C++ в Python.
- В обозревателе решений щелкните решение правой кнопкой мыши и последовательно выберите пункты Добавить>Новый проект. Решение Visual Studio может одновременно содержать проекты Python и C++ — это одно из преимуществ использования Visual Studio для Python.
- Выполните поиск C++, выберите Пустой проект, укажите либо superfastcode для первого проекта, либо superfastcode2 для второго проекта, а затем нажмите кнопку OK.
Совет Или, если вы установили собственные средства разработки Python в Visual Studio, можете начать с шаблона «Модуль расширения Python». В этом шаблоне уже имеется большая часть описанных здесь возможностей. Но в этом пошаговом руководстве начало работы с пустого проекта позволяет шаг за шагом продемонстрировать создание модуля расширения. Ознакомившись с процессом, в дальнейшем вы сможете использовать этот шаблон для экономии времени при написании собственных расширений.
Важно! Файл с расширением .cpp нужен, чтобы активировать страницы свойств C++ в последующих шагах.
- Для 64-разрядной среды выполнения Python активируйте конфигурацию x64.
- Для 32-разрядной среды выполнения Python следует активировать конфигурацию Win32.
Примечание. При создании собственных проектов вам понадобится настроить конфигурации и отладки, и выпуска. В этом уроке вы настроите только конфигурацию отладки и установите в ней использование сборки выпуска CPython. Эта конфигурация отключает некоторые функции отладки среды выполнения C++, в том числе утверждения. Для использования двоичных файлов отладки CPython (python_d.exe) требуются другие параметры.
Примечание. Если вы не видите вкладку C/C++ в свойствах проекта, значит, в проекте нет файлов, определенных как исходные файлы C/C++. Такая ситуация может возникнуть, если вы создали исходный файл без расширения .c или .cpp. Например, если в диалоговом окне создания элемента вы случайно введете module.coo вместо module.cpp, Visual Studio создаст файл, но не задаст для него тип Код C/C, что требуется для активации вкладки свойств C/C++. Такая неправильная идентификация сохранится, даже если вы переименуете файл и добавите расширение .cpp. Чтобы установить правильный тип файла, в Обозревателе решений щелкните файл правой кнопкой мыши и выберите пункт Свойства. Затем в качестве типа файлавыберите Код C/C++.
#include #include const double e = 2.7182818284590452353602874713527; double sinh_impl(double x) < return (1 - pow(e, (-2 * x))) / (2 * pow(e, -x)); >double cosh_impl(double x) < return (1 + pow(e, (-2 * x))) / (2 * pow(e, -x)); >double tanh_impl(double x) < return sinh_impl(x) / cosh_impl(x); >
Преобразование проекта C++ в расширения для Python
Чтобы преобразовать DLL-библиотеку C++ в расширение для Python, сначала нужно изменить экспортируемые методы для взаимодействия с типами Python. После этого добавьте функцию, экспортирующую модуль, а также определения методов модуля.
В последующих разделах объясняется, как выполнить эти действия с помощью расширений CPython и PyBind11.
Использование расширений CPython
Дополнительные сведения о коде, приведенном в этом разделе, см. в справочном руководстве по API Python/C, в частности на странице Объекты модуля. Не забудьте выбрать версию Python в раскрывающемся списке в правом верхнем углу.
-
В верхней части файла module.cpp включите Python.h.
#include
PyObject* tanh_impl(PyObject* /* unused module reference */, PyObject* o) < double x = PyFloat_AsDouble(o); double tanh_x = sinh_impl(x) / cosh_impl(x); return PyFloat_FromDouble(tanh_x); >
static PyMethodDef superfastcode_methods[] = < // The first property is the name exposed to Python, fast_tanh // The second is the C++ function with the implementation // METH_O means it takes a single PyObject argument < "fast_tanh", (PyCFunction)tanh_impl, METH_O, nullptr >, // Terminate the array with an object containing nulls. < nullptr, nullptr, 0, nullptr >>;
static PyModuleDef superfastcode_module = < PyModuleDef_HEAD_INIT, "superfastcode", // Module name to use with Python import statements "Provides some functions, but faster", // Module description 0, superfastcode_methods // Structure that defines the methods of the module >;
PyMODINIT_FUNC PyInit_superfastcode()
Использование PyBind11
Если вы выполнили действия из предыдущего раздела, вы наверняка заметили, что использовали много стандартного кода для создания необходимых структур модулей для кода C++. PyBind11 упрощает этот процесс с помощью макросов в файле заголовка C++, которые позволяют получить тот же результат с гораздо меньшим объемом кода.
Дополнительные сведения о коде в этом разделе см. в разделе Основы PyBind11.
- Установите PyBind11 с помощью pip: pip install pybind11 или py -m pip install pybind11 . Вы также можете установить PyBind11 с помощью окна «Окружения Python», а затем использовать его команду Открыть в PowerShell для выполнения следующего шага.
- В том же терминале выполните python -m pybind11 —includes или py -m pybind11 —includes . Это действие выводит список путей, которые следует добавить в свойство C/C++>Общие>Дополнительные каталоги включаемых файлов вашего проекта. Обязательно удалите префикс -I , если он присутствует.
- В верхнюю часть нового файла module.cpp, который не содержит изменения из предыдущего раздела, включите pybind11.h.
#include
namespace py = pybind11; PYBIND11_MODULE(superfastcode2, m)
Устранение ошибок компиляции
Модуль C++ может не компилироваться по следующим причинам.
- Ошибка: не удается обнаружить Python.h (E1696: не удается открыть исходный файл Python.h и/или C1083: не удается открыть включаемый файл: Python.h — не существует такого файла или каталога) Решение: проверьте, что путь в разделе C/C++>Общие>Дополнительные каталоги включаемых файлов в свойствах проекта указывает на папку include установки Python. См. шаг 6 в разделе Создание основного проекта C++. Дополнительные сведения о доступе к сведениям о конфигурации установки Python см . в документации по Python.
- Ошибка: не удается обнаружить библиотеки Python Решение: проверьте, что путь в разделе Компоновщик>Общие>Дополнительные каталоги библиотек в свойствах проекта указывает на папку libs установки Python. См. шаг 6 в разделе Создание основного проекта C++. Дополнительные сведения о доступе к сведениям о конфигурации установки Python см . в документации по Python.
- Ошибки компоновщика, связанные с решением целевой архитектуры: измените архитектуру проекта целевого объекта 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, щелкнув проект правой кнопкой мыши и выбрав пункт Добавить>Новый элемент.
- Выберите Файл C++ (. cpp), назначьте этому файлу имя setup.py и нажмите кнопку OK. Если файлу назначить имя с расширением .py, это позволяет Visual Studio распознать его как файл Python, несмотря на использование шаблона файла C++. Когда файл откроется в редакторе, вставьте в него следующий код в зависимости от метода расширения. Для расширений CPython (проект superfastcode)
from setuptools import setup, Extension sfc_module = Extension('superfastcode', sources = ['module.cpp']) setup( name='superfastcode', version='1.0', description='Python Package with superfastcode C++ extension', ext_modules=[sfc_module] )
Для PyBind11 (проект superfastcode2)
from setuptools import setup, Extension import pybind11 cpp_args = ['-std=c++11', '-stdlib=libc++', '-mmacosx-version-min=10.7'] sfc_module = Extension( 'superfastcode2', sources=['module.cpp'], include_dirs=[pybind11.get_include()], language='c++', extra_compile_args=cpp_args, ) setup( name='superfastcode2', version='1.0', description='Python package with superfastcode2 C++ extension (PyBind11)', ext_modules=[sfc_module], )
[build-system] requires = ["setuptools", "wheel", "pybind11"] build-backend = "setuptools.build_meta"
Совет Если пакет уже установлен, он появится в списке. Прежде чем продолжить, щелкните значок X, чтобы удалить его.
Совет Если установка завершается неудачно из-за ошибки, связанной с разрешениями, добавьте в конец —user и попробуйте выполнить команду еще раз.
Вызов библиотеки DLL из Python
После того как вы сделаете библиотеки DLL доступными для Python, как описано в предыдущем разделе, можно вызывать функции superfastcode.fast_tanh и superfastcode2.fast_tanh2 из кода Python и сравнивать их эффективность с реализацией Python. Чтобы вызвать библиотеку DLL, выполните следующие действия.
-
Добавьте следующие строки в файл .py, чтобы вызвать методы, экспортированные из библиотек DLL, и отобразить их выходные данные.
from superfastcode import fast_tanh test(lambda d: [fast_tanh(x) for x in d], '[fast_tanh(x) for x in d] (CPython C++ extension)') from superfastcode2 import fast_tanh2 test(lambda d: [fast_tanh2(x) for x in d], '[fast_tanh2(x) for x in d] (PyBind11 C++ extension)')
Примечание. Если команда Запуск без отладки отключена, в Обозревателе решений щелкните правой кнопкой мыши проект Python и выберите команду Назначить запускаемым проектом.
Убедитесь, что подпрограммы C++ выполняются примерно в 5–20 раз быстрее, чем реализация на Python. Обычно выводится следующий результат.
Running benchmarks with COUNT = 500000 [tanh(x) for x in d] (Python implementation) took 0.758 seconds [fast_tanh(x) for x in d] (CPython C++ extension) took 0.076 seconds [fast_tanh2(x) for x in d] (PyBind11 C++ extension) took 0.204 seconds
В выходных данных вы можете увидеть, что расширение PyBind11 не такое быстрое, как расширение CPython, хотя оно все равно должно работать быстрее, чем прямая реализация на Python. Это различие в основном связано с тем, что мы использовали вызов METH_O , который не поддерживает несколько параметров, имен параметров или аргументов ключевых слов. PyBind11 формирует несколько более сложный код, чтобы предоставить вызывающим субъектам интерфейс, более похожий на Python. Но так как тестовый код вызывает функцию 500 000 раз, в результате эти дополнительные временные затраты могут сильно возрасти!
Чтобы сократить временные затраты, можно переместить цикл for в машинный код. Этот подход предполагает использование протокола итератора (или типа py::iterable из PyBind11 в качестве параметра функции) для обработки каждого элемента. Удаление повторяющихся переходов между Python и C++ является эффективным способом сокращения времени, затрачиваемого на обработку последовательности.
Устранение ошибок импорта
Если при попытке импорта модуля вы получаете сообщение ImportError , для разрешения этой проблемы можно использовать один из следующих способов.
- При сборке с помощью ссылки на проект убедитесь, что свойства проекта C++ соответствуют среде Python, активированной для проекта Python, особенно каталоги Include и Library.
- Убедитесь, что ваш выходной файл имеет имя superfastcode.pyd. Файл с любым другим именем или расширением не будет импортирован.
- Если вы установили модуль с помощью файла setup.py, убедитесь, что вы выполнили команду pip в среде Python, активированной для вашего проекта Python. При развертывании среды Python в обозревателе решений должна отображаться запись для superfastcode.
Отладка кода C++
Visual Studio поддерживает совместную отладку кода на Python и C++. В этом разделе вы пошагово выполните процесс с использованием проекта superfastcode. Этот процесс такой же, как для проекта superfastcode2.
-
В Обозревателе решений щелкните проект Python правой кнопкой мыши, выберите Свойства, откройте вкладку Отладка и выберите Отладка>Разрешить отладку машинного кода.
Совет При включении отладки машинного кода окно вывода Python может немедленно закрываться сразу после завершения программы без обычной паузы с сообщением Для продолжения нажмите любую клавишу. Решение: чтобы после включения отладки машинного кода принудительно приостановить выполнение программы, добавьте параметр -i в поле Запуск>Аргументы интерпретатора на вкладке Отладка. Этот аргумент переводит интерпретатор Python в интерактивный режим после выполнения кода, и для закрытия окна интерпретатора необходимо будет нажать клавиши CTRL+Z, а затем ВВОД. Кроме того, если вы не против изменения кода Python, можно добавить в конец программы операторы import os и os.system(«pause») . Этот код дублирует исходный запрос с приостановкой.


Альтернативные подходы
Существуют различные способы для создания расширений Python, приведенные в таблице ниже. Способы CPython и PyBind11 , указанные в первых двух строках, рассматриваются в этой статье.
| Подход | 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.