Extender Python con librerías C++

Extender Python con librerías C++
extending cabecera

Añadiendo superpoderes personalizados

Ya mencioné en el tutorial introductorio de PyLab lo bueno que es Python añadiendo nuevas capacidades a su intérprete. En ese tutorial comentábamos las opciones que había para instalar diferentes librerías para cada sistema operativo, pero todas las librerías que instalábamos en aquel tutorial eran librerías que ya estaban hechas. En este tutorial vamos a aprender cómo hacer nuestras propias librerías. Así tendremos métodos que vayamos a usar a menudo desde el intérprete con tan sólo hacer un simple import miModulo . Además vamos un paso más allá y vamos a escribir la librería en C++.

Diferentes metodologías

Como casi en todo, existen varias maneras de hacer las cosas. Por un lado podemos utilizar directamente la Python/C API que nos proporcionan con la instalación de Python y que es el método que viene detallado en la documentación oficial, y por otro lado podremos usar "wrappers" (envolturas, si traducimos literalmente) como Boost o Swig. La principal diferencia radica en que usando Boost o Swig programaremos una librería en C++ como si no fuera para usarla con el interprete Python, luego le añadiríamos un trozo de código y ya todo quedaría listo para usarse. Y si usamos la Python/C API todo nuestro código aunque sea C++ va a estar orientado a ser una librería para Python y por tanto, debemos tener unos sólidos conocimientos de dicha API.

Nosotros en este tutorial vamos a optar por programar usando la Python/C API y vamos a realizar nuestra librería en un entorno linuxero. En Windows es parecido pero creo que con Linux vamos más al grano.

Contenido de nuestra librería

Lo primero es empezar nuestro código con #include <Python.h> , así tendremos los recursos de Python/C API a nuestra disposición. La librería se va a componer esencialmente de 3 bloques. El primer bloque es donde pondremos las definiciones de los métodos o funciones. Digo métodos porque recordemos que en Python todo se trata como objetos. Estos métodos tienen que estar definidos usando la Python/C API, por lo tanto debéis acudir a la página oficial para buscar más herramientas de las que explican aquí. Nosotros vamos a hacer un sencillo ejemplo y vamos a explicar sólo aquello que usemos de dicha API. El ejemplo va a ser crear una librería llamada "calculadora" que va a tener 2 metódos, un método de suma y otro de división. El método de suma nos mostrará la suma de dos números enteros que le demos como argumentos, pero sin que nos retorne el resultado. El código con el que empezamos nuestra calculadora.cpp y que define nuestro método quedaría así:

#include <Python.h> static PyObject* calculadora_suma(PyObject* self, PyObject* args) //Le pasamos siempre self y en este caso argumentos { int num1,num2;                                   //Variables donde vamos a recibir nuestros argumentos if (!PyArg_ParseTuple(args, "ii", &num1,&num2)) //Indicamos cómo debe interpretar los argumentos "ii" ->int, int return NULL;                                //La función retorna 1 si hay problemas con la interpretacion printf("Numeros introducidos %d + %d \n",num1, num2); int n = num1 + num2; printf("La suma es %d \n", n); Py_RETURN_NONE;                                //No retornamos nada }

De igual manera, si queremos hacer un método para esta librería calculadora que nos muestre por pantalla la división, este método quedará así:

static PyObject* calculadora_division(PyObject* self, PyObject* args) { float num1,num2; if (!PyArg_ParseTuple(args, "ff", &num1,&num2)) //"ff" -> float, float return NULL; printf("Numeros introducidos %f / %f \n",num1, num2); float n = num1 / num2; printf("La division es %f \n", n); Py_RETURN_NONE; }

Ya tenemos nuestros 2 métodos y, propongo como ejercicio de refuerzo al lector a hacer el método de resta y multiplicación. El siguiente bloque importante de código es el "mapeo" de estos métodos, aquí es donde le indicamos a Python el nombre que tienen los métodos así como los nombres con los que los podremos usar en el intérprete, si se pasan o no argumentos. Nuestro bloque para los métodos definidos anteriormente tendrá esta pinta:

static PyMethodDef calculadora_Methods[] = { {"suma", calculadora_suma, METH_VARARGS, "Dados 2 numeros muestra la suma"}, {"division", calculadora_division, METH_VARARGS, "Dados 2 numeros muestra la division"}, {NULL, NULL, 0, NULL} //Siempre añadir esta línea después de los métodos definidos por vosotros };

Y el último bloque es el de inicialización, es algo así como un constructor. El nuestro queda así:

PyMODINIT_FUNC initcalculadora(void) { (void) Py_InitModule("calculadora", calculadora_Methods); } Para que no haya líos podéis encontrar el código completo en github.

Compilando la librería

Aunque por comodidad usemos un IDE de programación como Visual Studio, CodeBLocks, etc. en vez de un editor de texto plano, en realidad el código no tenemos que compilarlo con estos programas. Podríamos hacerlo, pero para empezar nos dirán que Python.h no es un archivo ni un directorio y tendremos que indicarle donde está el archivo, etc. Es mucho más cómodo y rápido que usemos la librería setuptools de Python. Por lo tanto, cuando acabemos de editar nuestra calculadora.cpp guardamos y creamos un archivo setup.py en el mismo directorio cuyo contenido será el siguiente:

from distutils.core import setup, Extension module1 = Extension('calculadora', sources = ['calculadora.cpp']) setup (name = 'calculadora', version = '1.0', description = 'This is a demo package', ext_modules = [module1])

Luego abrimos una terminal, navegamos hasta nuestro directorio y utilizamos el siguiente comando:

python setup.py build

Si queremos instalar nuestra librería ya en el interprete debemos cambiar el "build" por "install". Yo prefiero hacer "build" y probar la librería antes de instalar. Si todo ha salido bien, el script nos habrá generado la libreŕia calculadora.so, dentro de una carpeta llamada "Build". Para probar nuestra librería debemos utilizar una terminal y navegar hasta el directorio que contenga la librería (yo lo que hago es mover el archivo calculadora.so al directorio donde está el setup.py y el calculadora.cpp y así tenemos nuestro terminal ya abierta de cuando compilé) y ejecutar los siguientes comandos:

python -c "import calculadora" python

Entonces empezamos a usar el interprete Python y podremos usar nuestros métodos con estas líneas:

import calculadora as calc calc.suma(5,3) calc.division(20,8)

Vídeo

Os dejo un vídeo con todos los pasos seguidos así como el resultado:

Como algún audaz lector ya se habrá imaginado dentro de poco haré un manual de como empotrar el interprete Python en una aplicación escrita en C++, sería lo opuesto a lo que hemos hecho en este tutorial.

¡Nos vemos en otro tutoriales sobre Python o Arduino!