ejecutable - Creación de prototipos con código Python antes de compilar
crear ejecutable python 3 (7)
He estado reflexionando sobre escribir una biblioteca de ajuste máximo por un tiempo. Conozco Python bastante bien y planeo implementar todo en Python para empezar, pero preveo que quizás tenga que volver a implementar algunas rutinas centrales en un lenguaje compilado eventualmente.
IIRC, una de las competencias originales de Python era como lenguaje de creación de prototipos, sin embargo, Python es bastante liberal al permitir que las funciones, funtores y objetos pasen a funciones y métodos, mientras que sospecho que no sucede lo mismo con decir C o Fortran.
¿Qué debo saber sobre el diseño de funciones / clases que, supongo, tendrán que interconectarse con el lenguaje compilado? ¿Y cuántos de estos problemas potenciales son tratados por bibliotecas como cTypes, bgen, SWIG , Boost.Python , Cython o Python SIP ?
Para este caso de uso particular, (una biblioteca de adaptación), me imagino que permite a los usuarios definir funciones matemáticas (Guassian, Lorentzian, etc.) como funciones de Python que luego pueden pasarse e interpretarse mediante la biblioteca de ajuste de código compilado. Pasar y regresar matrices también es esencial.
No he usado SWIG o SIP, pero creo que escribir wrappers de Python con boost.python es muy potente y relativamente fácil de usar.
No tengo claro cuáles son sus requisitos para pasar tipos entre C / C ++ y python, pero puede hacerlo fácilmente al exponer un tipo de C ++ a python, o al usar un argumento genérico boost :: python :: object para su API C ++. También puede registrar conversores para convertir automáticamente tipos de Python a tipos de C ++ y viceversa.
Si planeas usar boost.python, el tutorial es un buen lugar para comenzar.
He implementado algo similar a lo que necesitas. Tengo una función C ++ que acepta una función python y una imagen como argumentos, y aplica la función python a cada píxel de la imagen.
Image* unary(boost::python::object op, Image& im)
{
Image* out = new Image(im.width(), im.height(), im.channels());
for(unsigned int i=0; i<im.size(); i++)
{
(*out)[i] == extract<float>(op(im[i]));
}
return out;
}
En este caso, Image es un objeto C ++ expuesto a python (una imagen con píxeles float), y op es una función definida por python (o realmente cualquier objeto python con un atributo __call__). A continuación, puede utilizar esta función de la siguiente manera (suponiendo que unary se encuentra en la imagen llamada que también contiene Image y una función de carga):
import image
im = image.load(''somefile.tiff'')
double_im = image.unary(lambda x: 2.0*x, im)
En cuanto al uso de matrices con impulso, personalmente no he hecho esto, pero sé que la funcionalidad para exponer matrices a python usando boost está disponible, esto podría ser útil.
Python es bastante liberal al permitir que las funciones, los funtores y los objetos pasen a funciones y métodos, mientras que sospecho que no sucede lo mismo con decir C o Fortran.
En C no puede pasar una función como argumento a una función, pero puede pasar un puntero de función que es tan buena como una función.
No sé cuánto ayudaría cuando intentes integrar el código de C y Python, pero solo quería aclarar una idea errónea.
f2py (parte de numpy
) es una alternativa más simple a SWIG y boost.python para envolver el código de cifrado numérico de C / Fortran.
Finalmente una pregunta a la que realmente puedo ponerle una respuesta de valor :).
He investigado f2py, boost.python, swig, cython y pyrex para mi trabajo (doctorado en técnicas de medición óptica). Usé swig extensivamente, boost.python un poco y pyrex y cython mucho. También utilicé ctypes. Este es mi problema:
Descargo de responsabilidad : esta es mi experiencia personal. No estoy involucrado en ninguno de estos proyectos.
swig: no juega bien con c ++. Debería, pero nombrar problemas de manipulación en el paso de enlace fue un gran dolor de cabeza para mí en Linux y Mac OS X. Si tienes código C y quieres que interactúe con Python, es una buena solución. Envolví el GTS para mis necesidades y necesité escribir básicamente una biblioteca compartida C a la que me pude conectar. Yo no lo recomendaría.
Ctypes: escribí un contenedor libdc1394 (biblioteca de cámara IEEE) usando ctypes y fue una experiencia muy directa. Puede encontrar el código en https://launchpad.net/pydc1394 . Es mucho trabajo convertir encabezados en código python, pero luego todo funciona de manera confiable. Esta es una buena forma si desea interactuar con una biblioteca externa. Ctypes también está en stdlib de python, por lo que todos pueden usar su código de inmediato. Esta es también una buena manera de jugar con una nueva lib en python rápidamente. Puedo recomendarlo para interactuar con libs externos.
Boost.Python : muy agradable. Si ya tiene su propio código C ++ que desea usar en python, haga esto. Es muy fácil traducir estructuras de clase c ++ en estructuras de clase python de esta manera. Lo recomiendo si tiene el código de C ++ que necesita en Python.
Pyrex / Cython: usa Cython, no Pyrex. Período. Cython es más avanzado y más agradable de usar. Hoy en día, hago todo con cython que solía hacer con SWIG o Ctypes. También es la mejor manera si tienes un código python que se ejecuta demasiado lento. El proceso es absolutamente fantástico: convierte sus módulos de python en módulos de cython, los construye y sigue perfilando y optimizando como si todavía fuera python (no es necesario cambiar las herramientas). A continuación, puede aplicar tanto (o tan poco) código C mezclado con su código python. Esto es mucho más rápido que tener que reescribir partes completas de su aplicación en C; solo reescribe el bucle interno.
Tiempos: ctypes tiene la sobrecarga de llamada más alta (~ 700ns), seguido de boost.python (322ns), luego directamente por swig (290ns). Cython tiene la sobrecarga de llamadas más baja (124ns) y los mejores comentarios donde pasa el tiempo (soporte de cProfile!). Los números provienen de mi casilla que llama a una función trivial que devuelve un entero de un intérprete interactivo; Por lo tanto, la sobrecarga de importación del módulo no está temporizada, solo la sobrecarga de llamada de función es. Por lo tanto, es más fácil y más productivo obtener el código python rápidamente mediante el uso de perfiles y el uso de cython.
Resumen : para su problema, use Cython;). Espero que este resumen sea útil para algunas personas. Con gusto responderé cualquier pregunta restante.
Editar : Olvidé mencionar: para propósitos numéricos (es decir, conexión a NumPy) use Cython; tienen soporte para ello (porque básicamente desarrollan cython para este propósito). Entonces este debería ser otro +1 para su decisión.
En mi experiencia, hay dos formas fáciles de llamar al código C desde el código de Python. Hay otros enfoques, todos los cuales son más molestos y / o detallados.
El primero y más fácil es compilar un montón de código C como una biblioteca compartida separada y luego llamar funciones en esa biblioteca usando ctypes. Desafortunadamente, pasar cualquier cosa que no sean tipos de datos básicos no es trivial.
La segunda forma más fácil es escribir un módulo de Python en C y luego llamar funciones en ese módulo. Puedes pasar todo lo que quieras a estas funciones C sin tener que pasar por ningún aro. Y es fácil llamar a funciones o métodos de Python desde estas funciones C, como se describe aquí: https://docs.python.org/extending/extending.html#calling-python-functions-from-c
No tengo suficiente experiencia con SWIG para ofrecer comentarios inteligentes. Y aunque es posible hacer cosas como pasar objetos personalizados de Python a funciones C a través de ctypes, o definir nuevas clases de Python en C, estas cosas son molestas y prolijas, y recomiendo tomar uno de los dos enfoques descritos anteriormente.
La mejor forma de planificar una transición final al código compilado es escribir las partes sensibles al rendimiento como un módulo de funciones simples en un estilo funcional (sin estado y sin efectos secundarios), que acepta y devuelve tipos de datos básicos.
Esto proporcionará un mapeo uno a uno desde su código de prototipo de Python al eventual código compilado, y le permitirá usar ctypes fácilmente y evitar un montón de dolores de cabeza.
Para el ajuste máximo, casi seguramente necesitará usar matrices, lo que complicará un poco las cosas, pero todavía es muy factible con los tipos.
Si realmente desea utilizar estructuras de datos más complicadas, o modificar los argumentos pasados, la interfaz estándar de C-extensión de SWIG o Python le permitirá hacer lo que quiera, pero con cierta cantidad de molestias.
Por lo que estás haciendo, es posible que también quieras ver NumPy , que podría hacer parte del trabajo que querrías llevar a C, y también ofrecer ayuda adicional para mover datos entre Python y C.