Каким будет самый быстрый способ создания привязки Python к библиотеке C или С++?
(Я использую Windows, если это имеет значение.)
Каким будет самый быстрый способ создания привязки Python к библиотеке C или С++?
(Я использую Windows, если это имеет значение.)
Вы должны взглянуть на Boost.Python. Вот краткое введение, взятое с их сайта:
Библиотека Boost Python - это среда для взаимодействия Python и С++. Это позволяет вам быстро и легко выставлять классы С++ функций и объектов на Python, и наоборот, без использования специальных инструменты - просто ваш компилятор С++. Он предназначен для переноса интерфейсов на С++ неинтрузивно, так что вам не нужно менять код на С++ все, чтобы обернуть его, сделав Boost.Python идеальным для разоблачения Сторонние библиотеки для Python. Использование библиотеки передовых методы метапрограммирования упрощают его синтаксис для пользователей, так что код обертки принимает вид своего рода декларативного интерфейса (IDL).
ctypes является частью стандартной библиотеки, поэтому он более стабилен и широко доступен, чем swig, что всегда вызывало у меня проблемы.
С помощью ctypes вам нужно удовлетворить любую зависимость времени компиляции от python, и ваша привязка будет работать на любом питоне, имеющем ctypes, а не только на том, с которым он был скомпилирован.
Предположим, у вас есть простой пример класса C++, с которым вы хотите поговорить в файле с именем foo.cpp:
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Поскольку ctypes может общаться только с функциями C, вам необходимо предоставить тех, кто объявляет их как внешние "C"
extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo){ foo->bar(); }
}
Далее вы должны скомпилировать это в общую библиотеку
g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
И, наконец, вы должны написать свою оболочку Python (например, в fooWrapper.py)
from ctypes import cdll
lib = cdll.LoadLibrary('./libfoo.so')
class Foo(object):
def __init__(self):
self.obj = lib.Foo_new()
def bar(self):
lib.Foo_bar(self.obj)
Если у вас есть это, вы можете назвать это как
f = Foo()
f.bar() #and you will see "Hello" on the screen
Самый быстрый способ сделать это - SWIG.
Пример из SWIG tutorial:
/* File : example.c */
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
Файл интерфейса:
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}
extern int fact(int n);
Построение модуля Python в Unix:
swig -python example.i
gcc -fPIC -c example.c example_wrap.c -I/usr/local/include/python2.7
gcc -shared example.o example_wrap.o -o _example.so
Использование:
>>> import example
>>> example.fact(5)
120
Обратите внимание, что у вас должен быть python-dev. Также в некоторых системах файлы заголовков python будут находиться в /usr/include/python 2.7 на основе того, как вы его установили.
Из учебника:
SWIG - довольно полный компилятор С++ с поддержкой почти всех функций языка. Это включает в себя предварительную обработку, указатели, классы, наследование и даже шаблоны С++. SWIG также может использоваться для упаковки структур и классов в прокси-классы на целевом языке - выставляя основные функции очень естественным образом.
Я начал свое путешествие по связыванию Python ↔ С++ с этой страницы с целью связывания высокоуровневых типов данных (многомерных векторов STL с списками Python): -)
Попробовав решения, основанные на ctypes и boost. python (и не являюсь инженером-программистом), я нашел их сложными, когда требуется привязка данных высокого уровня, в то время как я нашел SWIG гораздо проще для таких случаев.
В этом примере используется SWIG, и он был протестирован в Linux (но SWIG доступен и широко используется в Windows).
Цель состоит в том, чтобы сделать С++-функцию доступной для Python, которая принимает матрицу в виде 2D STL-вектора и возвращает среднее значение для каждой строки (в качестве 1D-вектора STL).
Код в С++ ( "code.cpp" ) выглядит следующим образом:
#include <vector>
#include "code.h"
using namespace std;
vector<double> average (vector< vector<double> > i_matrix) {
// Compute average of each row..
vector <double> averages;
for (int r = 0; r < i_matrix.size(); r++){
double rsum = 0.0;
double ncols= i_matrix[r].size();
for (int c = 0; c< i_matrix[r].size(); c++){
rsum += i_matrix[r][c];
}
averages.push_back(rsum/ncols);
}
return averages;
}
Эквивалентный заголовок ( "code.h" ):
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
Сначала мы скомпилируем код С++ для создания объектного файла:
g++ -c -fPIC code.cpp
Затем мы определяем файл определения интерфейса SWIG ( "code.i" ) для наших функций С++.
%module code
%{
#include "code.h"
%}
%include "std_vector.i"
namespace std {
/* On a side note, the names VecDouble and VecVecdouble can be changed, but the order of first the inner vector matters! */
%template(VecDouble) vector<double>;
%template(VecVecdouble) vector< vector<double> >;
}
%include "code.h"
Используя SWIG, мы генерируем исходный код интерфейса С++ из файла определения интерфейса SWIG.
swig -c++ -python code.i
Наконец, мы скомпилируем сгенерированный исходный файл интерфейса С++ и соединим все вместе для создания общей библиотеки, которая напрямую импортируется Python (значение "_" ):
g++ -c -fPIC code_wrap.cxx -I/usr/include/python2.7 -I/usr/lib/python2.7
g++ -shared -Wl,-soname,_code.so -o _code.so code.o code_wrap.o
Теперь мы можем использовать эту функцию в скриптах Python:
#!/usr/bin/env python
import code
a= [[3,5,7],[8,10,12]]
print a
b = code.average(a)
print "Assignment done"
print a
print b
Проверьте Pyrex или Cython. Это Python-подобные языки для взаимодействия между C/C++ и Python.
Существует также pybind11
, который похож на облегченную версию Boost.Python и совместим со всеми современными компиляторами C++:
В этой статье, утверждающей, что Python - это все, что нужно ученому, в основном говорится: Первый прототип всего на Python. Затем, когда вам нужно ускорить часть, используйте SWIG и переведите эту часть на C.
Для современных C++ используйте cppyy: http://cppyy.readthedocs.io/en/latest/
Он основан на Cling, интерпретаторе C++ для Clang/LLVM. Привязки выполняются во время выполнения, и дополнительный промежуточный язык не требуется. Благодаря Clang он поддерживает C++ 17.
Установите его используя pip:
$ pip install cppyy
Для небольших проектов просто загрузите соответствующую библиотеку и интересующие вас заголовки. Например, возьмите код из примера ctypes, который приведен ниже, но разделите его на разделы заголовка и кода:
$ cat foo.h
class Foo {
public:
void bar();
};
$ cat foo.cpp
#include "foo.h"
#include <iostream>
void Foo::bar() { std::cout << "Hello" << std::endl; }
Скомпилируйте это:
$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
и использовать это:
$ python
>>> import cppyy
>>> cppyy.include("foo.h")
>>> cppyy.load_library("foo")
>>> from cppyy.gbl import Foo
>>> f = Foo()
>>> f.bar()
Hello
>>>
Большие проекты поддерживаются автоматической загрузкой подготовленной информации об отражении и фрагментов cmake для их создания, так что пользователи установленных пакетов могут просто запустить:
$ python
>>> import cppyy
>>> f = cppyy.gbl.Foo()
>>> f.bar()
Hello
>>>
Благодаря LLVM возможны расширенные функции, такие как автоматическое создание шаблона. Чтобы продолжить пример:
>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
>>> v.push_back(f)
>>> len(v)
1
>>> v[0].bar()
Hello
>>>
Примечание: я автор cppyy.
Я никогда не использовал его, но я слышал хорошие вещи о ctypes. Если вы пытаетесь использовать его с С++, обязательно избегайте манипуляции имени с помощью extern "C"
. Спасибо за комментарий, Флориан Бёш.
Я думаю, что cffi для python может быть вариантом.
Целью является вызов кода C из Python. Вы должны быть в состоянии сделать это без изучения третьего языка: каждая альтернатива требует от вас изучать их собственный язык (Cython, SWIG) или API (ctypes). Поэтому мы попробовали предположить, что вы знаете Python и C и сводите к минимуму дополнительные биты API, который вам нужно изучить.
Один из официальных документов Python содержит подробности о расширении Python с использованием C/C++. Даже без использования SWIG, он довольно прост и отлично работает на Windows.
Вопрос в том, как вызвать функцию C из Python, если я правильно понял. Тогда лучшим выбором будет Ctypes (кстати, переносимый во всех вариантах Python).
>>> from ctypes import *
>>> libc = cdll.msvcrt
>>> print libc.time(None)
1438069008
>>> printf = libc.printf
>>> printf("Hello, %s\n", "World!")
Hello, World!
14
>>> printf("%d bottles of beer\n", 42)
42 bottles of beer
19
Для подробного руководства вы можете обратиться к моей статье в блоге.
Сначала вы должны решить, какова ваша конкретная цель. Официальная документация Python по расширению и внедрению интерпретатора Python была упомянута выше, я могу добавить хороший обзор бинарных расширений. Варианты использования можно разделить на 3 категории:
Чтобы дать более широкую перспективу для других заинтересованных лиц, и поскольку ваш первоначальный вопрос немного расплывчатый ("к библиотеке C или C++"), я думаю, что эта информация может быть вам интересна. По ссылке выше вы можете прочитать о недостатках использования бинарных расширений и их альтернатив.
Помимо других предложенных ответов, если вы хотите ускоритель, вы можете попробовать Numba. Он работает "путем генерации оптимизированного машинного кода с использованием инфраструктуры компилятора LLVM во время импорта, во время выполнения или статически (с помощью включенного инструмента pycc)".
Cython - определенно правильный путь, если только вы не планируете писать обертки Java, в этом случае SWIG может быть предпочтительнее.
Я рекомендую использовать runcython
командной строки runcython
, она делает процесс использования Cython чрезвычайно простым. Если вам нужно передать структурированные данные в C++, взгляните на библиотеку Google protobuf, это очень удобно.
Вот минимальные примеры, которые я сделал, которые используют оба инструмента:
https://github.com/nicodjimenez/python2cpp
Надеюсь, что это может быть полезной отправной точкой.
Вы можете использовать Scapix Language Bridge для автоматической генерации привязок Python непосредственно из заголовков C++ как части сборки.
Добавьте вызов в scapix_bridge_headers()
в файл CMakeLists.txt, а затем создайте свой проект с помощью cmake -DSCAPIX_BRIDGE=python
. Смотрите полный пример.
Отказ от ответственности: я являюсь автором Scapix Language Bridge.