¿Llamando a C / C ++ desde Python?

¿Cuál sería la forma más rápida de construir un enlace de Python a una biblioteca C o C ++?

(Estoy usando Windows si esto importa.)

Deberías echar un vistazo a Boost.Python . Aquí está la breve introducción tomada de su sitio web:

La biblioteca Boost Python es un marco para la interconexión de Python y C ++. Le permite exponer de forma rápida y sin problemas las funciones y objetos de las clases de C ++ a Python, y viceversa, sin usar herramientas especiales, solo su comstackdor de C ++. Está diseñado para envolver las interfaces de C ++ de manera no intrusiva, de modo que no tenga que cambiar el código de C ++ para envolverlo, por lo que Boost.Python es ideal para exponer bibliotecas de terceros a Python. El uso de la biblioteca de técnicas avanzadas de metaprogtwigción simplifica su syntax para los usuarios, de modo que el código de ajuste toma la apariencia de un tipo de lenguaje de definición de interfaz declarativa (IDL).

ctypes forma parte de la biblioteca estándar y, por lo tanto, es más estable y está más disponible que el swig , que siempre me ha dado problemas .

Con ctypes, debe satisfacer cualquier dependencia de tiempo de comstackción en python, y su enlace funcionará en cualquier python que tenga ctypes, no solo contra el que se compiló.

Supongamos que tiene una clase de ejemplo de C ++ simple con la que desea hablar en un archivo llamado foo.cpp:

#include  class Foo{ public: void bar(){ std::cout << "Hello" << std::endl; } }; 

Como los ctypes solo pueden comunicarse con las funciones de C, debe proporcionarles las que las declaran como "C" externas

 extern "C" { Foo* Foo_new(){ return new Foo(); } void Foo_bar(Foo* foo){ foo->bar(); } } 

Luego tienes que comstackr esto en una biblioteca compartida.

 g++ -c -fPIC foo.cpp -o foo.o g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o 

Y finalmente tienes que escribir tu envoltorio de python (por ejemplo, en 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) 

Una vez que tengas eso puedes llamarlo como

 f = Foo() f.bar() #and you will see "Hello" on the screen 

La forma más rápida de hacerlo es utilizar SWIG .

Ejemplo de tutorial SWIG:

 /* File : example.c */ int fact(int n) { if (n <= 1) return 1; else return n*fact(n-1); } 

Archivo de interfaz:

 /* example.i */ %module example %{ /* Put header files here or function declarations like below */ extern int fact(int n); %} extern int fact(int n); 

Construyendo un módulo Python en 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 

Uso:

 >>> import example >>> example.fact(5) 120 

Ten en cuenta que debes tener python-dev. También en algunos sistemas, los archivos de encabezado de python estarán en /usr/include/python2.7 según la forma en que lo haya instalado.

Desde el tutorial:

SWIG es un comstackdor de C ++ bastante completo con soporte para casi todas las características de idioma. Esto incluye preprocesamiento, punteros, clases, herencia e incluso plantillas de C ++. SWIG también se puede usar para empaquetar estructuras y clases en clases proxy en el idioma de destino, exponiendo la funcionalidad subyacente de una manera muy natural.

Comencé mi viaje en el enlace Python <-> C ++ desde esta página, con el objective de vincular tipos de datos de alto nivel (vectores STL multidimensionales con listas de Python) 🙂

Después de haber probado las soluciones basadas tanto en ctypes como boost.python (y no ser un ingeniero de software) las encontré complejas cuando se requiere un enlace de tipos de datos de alto nivel, mientras que para SWIG SWIG me parece mucho más simple.

Este ejemplo utiliza SWIG, y se ha probado en Linux (pero SWIG está disponible y se usa ampliamente en Windows también).

El objective es poner a disposición de Python una función C ++ que tome una matriz en forma de un vector 2D STL y devuelva un promedio de cada fila (como un vector 1D STL).

El código en C ++ (“code.cpp”) es el siguiente:

 #include  #include "code.h" using namespace std; vector average (vector< vector > i_matrix) { // Compute average of each row.. vector  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; } 

El encabezado equivalente ("code.h") es:

 #ifndef _code #define _code #include  std::vector average (std::vector< std::vector > i_matrix); #endif 

Primero comstackmos el código C ++ para crear un archivo de objeto:

 g++ -c -fPIC code.cpp 

Luego definimos un archivo de definición de interfaz SWIG ("code.i") para nuestras funciones C ++.

 %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; %template(VecVecdouble) vector< vector >; } %include "code.h" 

Usando SWIG, generamos un código fuente de la interfaz C ++ a partir del archivo de definición de la interfaz SWIG.

 swig -c++ -python code.i 

Finalmente, comstackmos el archivo fuente de la interfaz de C ++ generado y enlazamos todo para generar una biblioteca compartida que Python puede importar directamente (lo que importa es "_"):

 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 

Ahora podemos usar la función en los scripts de 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 

Echa un vistazo a pyrex o Cython . Son lenguajes similares a los de Python para interactuar entre C / C ++ y Python.

También está pybind11 , que es como una versión ligera de Boost.Python y compatible con todos los comstackdores C ++ modernos:

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

Este documento, que afirma que Python es todo lo que un científico necesita , básicamente dice: Primer prototipo de todo en Python. Luego, cuando necesite acelerar una parte, use SWIG y traduzca esta parte a C.

Nunca lo he usado pero he escuchado cosas buenas sobre ctypes . Si está intentando usarlo con C ++, asegúrese de evadir la manipulación de nombres a través de la extern "C" . Gracias por el comentario, Florian Bösch.

Creo que cffi para python puede ser una opción.

El objective es llamar al código C de Python. Debería poder hacerlo sin aprender un tercer idioma: cada alternativa requiere que aprenda su propio idioma (Cython, SWIG) o API (ctypes). Así que intentamos asumir que conoces Python y C y minimizar los bits extra de API que necesitas aprender.

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

Para C ++ moderno, use cppyy: http://cppyy.readthedocs.io/en/latest/

Se basa en Cling, el intérprete de C ++ para Clang / LLVM. Los enlaces están en tiempo de ejecución y no es necesario ningún idioma intermedio adicional. Gracias a Clang, soporta C ++ 17.

Instálalo usando pip:

  $ pip install cppyy 

Para proyectos pequeños, simplemente cargue la biblioteca relevante y los encabezados que le interesan. Por ejemplo, tome el código del ejemplo de ctypes en este hilo, pero divídalo en las secciones de encabezado y código:

  $ cat foo.h class Foo { public: void bar(); }; $ cat foo.cpp #include "foo.h" #include  void Foo::bar() { std::cout << "Hello" << std::endl; } 

Comstackrlo

  $ g++ -c -fPIC foo.cpp -o foo.o $ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o 

y úsalo:

  $ python >>> import cppyy >>> cppyy.include("foo.h") >>> cppyy.load_library("foo") >>> from cppyy.gbl import Foo >>> f = Foo() >>> f.bar() Hello >>> 

Los proyectos grandes son compatibles con la carga automática de información de reflexión preparada y los fragmentos de cmake para crearlos, de modo que los usuarios de paquetes instalados puedan simplemente ejecutar:

  $ python >>> import cppyy >>> f = cppyy.gbl.Foo() >>> f.bar() Hello >>> 

Gracias a LLVM, son posibles funciones avanzadas, como la creación automática de instancias de plantillas. Para continuar con el ejemplo:

  >>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]() >>> v.push_back(f) >>> len(v) 1 >>> v[0].bar() Hello >>> 

Nota: Soy el autor de cppyy.

Uno de los documentos oficiales de Python contiene detalles sobre la extensión de Python utilizando C / C ++ . Incluso sin el uso de SWIG , es bastante sencillo y funciona perfectamente bien en Windows.

La pregunta es cómo llamar a una función C desde Python, si entendí correctamente. Entonces, las mejores opciones son Ctypes (BTW portátil en todas las variantes de 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 

Para obtener una guía detallada, puede consultar el artículo de mi blog .

Cython es definitivamente el camino a seguir, a menos que prevea escribir envoltorios Java, en cuyo caso SWIG puede ser preferible.

Recomiendo usar la utilidad de línea de comando runcython , hace que el proceso de usar Cython sea extremadamente fácil. Si necesita pasar datos estructurados a C ++, eche un vistazo a la biblioteca protobuf de Google, es muy conveniente.

Aquí hay unos ejemplos mínimos que hice que usan ambas herramientas:

https://github.com/nicodjimenez/python2cpp

Espero que pueda ser un punto de partida útil.

Primero debes decidir cuál es tu propósito particular. La documentación oficial de Python sobre la extensión e incrustación del intérprete de Python se mencionó anteriormente, puedo agregar una buena descripción general de las extensiones binarias . Los casos de uso se pueden dividir en 3 categorías:

  • módulos de acelerador : para ejecutarse más rápido que el código Python puro equivalente se ejecuta en CPython.
  • Módulos de envoltura : para exponer las interfaces C existentes al código Python.
  • acceso al sistema de bajo nivel : para acceder a las funciones de nivel inferior del tiempo de ejecución de CPython, el sistema operativo o el hardware subyacente.

Con el fin de ofrecer una perspectiva más amplia para otros interesados ​​y, dado que su pregunta inicial es un poco vaga (“para una biblioteca C o C ++”), creo que esta información podría ser interesante para usted. En el enlace anterior puede leer sobre las desventajas de usar extensiones binarias y sus alternativas.

Aparte de las otras respuestas sugeridas, si desea un módulo acelerador, puede probar Numba . Funciona “al generar código de máquina optimizado usando la infraestructura del comstackdor LLVM en el momento de la importación, el tiempo de ejecución o estáticamente (usando la herramienta pycc incluida)”.