Каким будет самый быстрый способ создания привязки Python к библиотеке C или С++?
(Я использую Windows, если это имеет значение.)
Вы должны взглянуть на Boost.Python. Вот краткое введение, взятое с их сайта:
Библиотека Boost Python - это среда для взаимодействия Python и С++. Это позволяет вам быстро и легко выставлять классы С++ функций и объектов на Python, и наоборот, без использования специальных инструменты - просто ваш компилятор С++. Он предназначен для переноса интерфейсов на С++ неинтрузивно, так что вам не нужно менять код на С++ все, чтобы обернуть его, сделав Boost.Python идеальным для разоблачения Сторонние библиотеки для Python. Использование библиотеки передовых методы метапрограммирования упрощают его синтаксис для пользователей, так что код обертки принимает вид своего рода декларативного интерфейса (IDL).
Мне нравится ctypes много, swig всегда вызывал проблемы . Кроме того, ctypes имеет то преимущество, что вам не нужно удовлетворять какой-либо зависимости времени компиляции от python, и ваша привязка будет работать на любом python, который имеет ctypes, а не только тот, который был скомпилирован.
Предположим, у вас есть простой класс класса С++, с которым вы хотите поговорить в файле foo.cpp:
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Так как ctypes может разговаривать только с функциями C, вам нужно предоставить те, которые объявляют их как extern "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
Я никогда не использовал его, но я слышал хорошие вещи о ctypes. Если вы пытаетесь использовать его с С++, обязательно избегайте манипуляции имени с помощью extern "C"
. Спасибо за комментарий, Флориан Бёш.
Эта статья, претендующая на то, что Python будет всем ученым, нуждается в, в основном говорит: Первый прототип всего в Python. Затем, когда вам нужно ускорить часть, используйте SWIG и переведите эту часть на C.
Существует также pybind11
, который похож на облегченную версию Boost и совместим со всеми современными компиляторами С++:
Я думаю, что cffi для python может быть вариантом.
Целью является вызов кода C из Python. Вы должны быть в состоянии сделать это без изучения третьего языка: каждая альтернатива требует от вас изучать их собственный язык (Cython, SWIG) или API (ctypes). Поэтому мы попробовали предположить, что вы знаете Python и C и сводите к минимуму дополнительные биты API, который вам нужно изучить.
Cython - это, безусловно, путь, если вы не ожидаете писать Java-обертки, и в этом случае SWIG может быть предпочтительнее.
Я рекомендую использовать утилиту командной строки runcython
, это делает процесс использования Cython чрезвычайно простым. Если вам нужно передать структурированные данные на С++, взгляните на библиотеку Google protobuf, это очень удобно.
Ниже приведены минимальные примеры, в которых используются оба инструмента:
https://github.com/nicodjimenez/python2cpp
Надеюсь, что это может быть полезной отправной точкой.
Вопрос в том, как вызвать функцию C из Python, если я правильно понял. Тогда лучшим вариантом являются Ctypes (BTW, переносимый по всем вариантам 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 с использованием C/С++. Даже без использования SWIG его довольно просто и отлично работает в Windows.
Сначала вы должны решить, какова ваша конкретная цель. Официальная документация Python по расширению и внедрению интерпретатора Python была упомянута выше, я могу добавить хороший обзор двоичных расширений. Варианты использования можно разделить на 3 категории:
Чтобы дать более широкую перспективу другим заинтересованным лицам, и поскольку ваш первоначальный вопрос немного расплывчатый ( "в библиотеке C или С++" ), я думаю, эта информация может быть вам интересна. По ссылке выше вы можете прочитать о недостатках использования двоичных расширений и их альтернатив.
Помимо других предложенных ответов, если вы хотите использовать модуль ускорителя, вы можете попробовать Numba. Он работает "путем создания оптимизированного машинного кода с использованием инфраструктуры компилятора LLVM во время импорта, времени выполнения или статически (с использованием встроенного инструмента pycc)".