Вызов C / C ++ из Python?

343

Каким будет самый быстрый способ создания привязки Python к библиотеке C или С++?

(Я использую Windows, если это имеет значение.)

Теги:

13 ответов

95
Лучший ответ

Вы должны взглянуть на Boost.Python. Вот краткое введение, взятое с их сайта:

Библиотека Boost Python - это среда для взаимодействия Python и С++. Это позволяет вам быстро и легко выставлять классы С++ функций и объектов на Python, и наоборот, без использования специальных инструменты - просто ваш компилятор С++. Он предназначен для переноса интерфейсов на С++ неинтрузивно, так что вам не нужно менять код на С++ все, чтобы обернуть его, сделав Boost.Python идеальным для разоблачения Сторонние библиотеки для Python. Использование библиотеки передовых методы метапрограммирования упрощают его синтаксис для пользователей, так что код обертки принимает вид своего рода декларативного интерфейса (IDL).

  • 0
    Boost.Python - одна из наиболее удобных для пользователя библиотек в Boost, для простого API вызова функций она довольно проста и предоставляет шаблон, который вам придется написать самостоятельно. Это немного сложнее, если вы хотите представить объектно-ориентированный API.
  • 3
    Boost.Python - худшее, что можно себе представить. Для каждой новой машины и с каждым обновлением это связано с проблемами связывания.
407

Мне нравится 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
  • 13
    Это в значительной степени то, что boost.python делает для вас одним вызовом функции.
  • 179
    ctypes находится в стандартной библиотеке Python, Swig и Boost нет. Swig и boost полагаются на модули расширения и поэтому привязаны к второстепенным версиям Python, которые не являются независимыми общими объектами. Создание swig или boost-оберток может быть проблемой, ctypes не предъявляет никаких требований к сборке.
Показать ещё 14 комментариев
36

Самый быстрый способ сделать это - 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 также может использоваться для упаковки структур и классов в прокси-классы на целевом языке - выставляя основные функции очень естественным образом.

  • 0
    Запуск в swig: невозможно выполнить ошибку двоичного файла, используя тот же код. Кажется, проблема с моей стороны, но любые указатели будут полезны.
  • 1
    @iDev звучит как отдельный вопрос
29

Я начал свое путешествие по связыванию 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
  • 0
    Реальная реализация, в которой в коде C ++ stl-векторы передаются как неконстантные ссылки и, следовательно, доступны через python в качестве выходных параметров: lobianco.org/antonello/personal:portfolio:portopt
21

Отъезд pyrex или Cython. Это Python-подобные языки для взаимодействия между C/С++ и Python.

  • 0
    +1 за Cython! Я не пробовал cffi, поэтому не могу сказать, что лучше, но у меня был очень хороший опыт работы с Cython - вы все еще пишете код Python, но можете использовать в нем C. Мне было довольно сложно настроить процесс сборки с помощью Cython, что я позже объяснил в блоге: martinsosic.com/development/2016/02/08/…
  • 0
    Возможно, вы захотите улучшить ответ, чтобы больше не быть ответом только по ссылке.
13

Я никогда не использовал его, но я слышал хорошие вещи о ctypes. Если вы пытаетесь использовать его с С++, обязательно избегайте манипуляции имени с помощью extern "C". Спасибо за комментарий, Флориан Бёш.

9

Эта статья, претендующая на то, что Python будет всем ученым, нуждается в, в основном говорит: Первый прототип всего в Python. Затем, когда вам нужно ускорить часть, используйте SWIG и переведите эту часть на C.

8

Существует также pybind11, который похож на облегченную версию Boost и совместим со всеми современными компиляторами С++:

https://pybind11.readthedocs.io/en/latest/

8

Я думаю, что cffi для python может быть вариантом.

Целью является вызов кода C из Python. Вы должны быть в состоянии сделать это без изучения третьего языка: каждая альтернатива требует от вас изучать их собственный язык (Cython, SWIG) или API (ctypes). Поэтому мы попробовали предположить, что вы знаете Python и C и сводите к минимуму дополнительные биты API, который вам нужно изучить.

http://cffi.readthedocs.org/en/release-0.7/

  • 2
    Я думаю, что это может только вызвать c (не c ++), все еще +1 (мне действительно нравится cffi).
4

Cython - это, безусловно, путь, если вы не ожидаете писать Java-обертки, и в этом случае SWIG может быть предпочтительнее.

Я рекомендую использовать утилиту командной строки runcython, это делает процесс использования Cython чрезвычайно простым. Если вам нужно передать структурированные данные на С++, взгляните на библиотеку Google protobuf, это очень удобно.

Ниже приведены минимальные примеры, в которых используются оба инструмента:

https://github.com/nicodjimenez/python2cpp

Надеюсь, что это может быть полезной отправной точкой.

3

Вопрос в том, как вызвать функцию 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

Для подробного руководства вы можете обратиться к моей статье в блоге.

  • 0
    Возможно, стоит отметить, что хотя ctypes являются переносимыми, для вашего кода требуется специфическая для Windows библиотека C.
3

В одном из официальных документов Python содержится подробная информация о расширении Python с использованием C/С++. Даже без использования SWIG его довольно просто и отлично работает в Windows.

2

Сначала вы должны решить, какова ваша конкретная цель. Официальная документация Python по расширению и внедрению интерпретатора Python была упомянута выше, я могу добавить хороший обзор двоичных расширений. Варианты использования можно разделить на 3 категории:

  • модули ускорителя: для запуска быстрее, чем эквивалентный чистый код Python выполняется в CPython.
  • оберточные модули: чтобы открыть существующие C-интерфейсы для кода Python.
  • низкоуровневый доступ к системе: для доступа к функциям нижнего уровня среды выполнения CPython, операционной системы или базового оборудования.

Чтобы дать более широкую перспективу другим заинтересованным лицам, и поскольку ваш первоначальный вопрос немного расплывчатый ( "в библиотеке C или С++" ), я думаю, эта информация может быть вам интересна. По ссылке выше вы можете прочитать о недостатках использования двоичных расширений и их альтернатив.

Помимо других предложенных ответов, если вы хотите использовать модуль ускорителя, вы можете попробовать Numba. Он работает "путем создания оптимизированного машинного кода с использованием инфраструктуры компилятора LLVM во время импорта, времени выполнения или статически (с использованием встроенного инструмента pycc)".

Ещё вопросы

Сообщество Overcoder
Наверх
Меню