Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: sukokiin en 3 Enero 2020, 14:52 pm



Título: Extender Python con C y C++
Publicado por: sukokiin en 3 Enero 2020, 14:52 pm
qf60SP5-YLQ

SI damos un pequeño repaso de manera simple entendemos que Python al tratarse de un lenguaje interpretado se traduce a tiempo real de ejecución, es decir, se traduce linea uno y se ejecuta, se traduce linea dos y se ejecuta, lenguajes interpretados van traduciendo las instrucciones a tiempo de ejecucion, en cambio lenguajes compilados como C y C++ son traducidos en tiempo de compilación, para mas tarde ser ejecutado todo el conjunto en lenguaje maquina de una.
Es decir C traduce el lenguaje de alto nivel a lenguaje maquina y luego ejecuta el programa, en cambio Python ejecuta el programa mientras va traduciendo a tiempo real el lenguaje maquina.
Esto ocasiona lentitud en lenguajes Interpretados, y por eso entra en juego una maravillosa API que permite extender programas de Python haciéndolos mas veloces mediante llamadas a funciones e métodos ya compilados en C y C++.

Como haríamos eso?

Lo primero es tener instalado "Python.h" lo haremos mediante el siguiente comando para LInux, Ubuntu, etc...
(En caso de WIndows, VIsual Studio por ejemplo,Propiedades -> Opciones de Proyecto -> C/C++ -> General -> Directorios de inclusión adicionales)
Recomiendo la programación de extensiones solamente en Linux, Ubuntu, Debian o derivados...

Código
  1. sudo apt-get install python-dev -y

SI no tenemos instalado C++ lo podemos instalar mediante ->

Código
  1. sudo apt-get install g++

Una vez instalado todo simplemente escribiremos nuestra primera extensión de Python

Código
  1. #include "Python.h"
  2. #include "iostream"
  3.  
  4. static PyObject* Operacion_Suma(PyObject* self, PyObject* args)
  5. {
  6. float num1,num2;
  7. float resultado;
  8. std::cout << "Inserte dos numeros separados por un espacio en blanco para realizar una suma.\n";
  9. std::cin >> num1;
  10. std::cin >> num2;
  11. resultado = num1 + num2;
  12. std::cout << "El resultado de la suma " << num1 << " + " << num2 << " es = " << resultado;
  13.  
  14. Py_RETURN_NONE;
  15. }
  16.  
  17. static PyObject* Operacion_Resta(PyObject* self, PyObject* args)
  18. {
  19. float num1,num2;
  20. float resultado;
  21. std::cout << "Inserte dos numeros separados por un espacio en blanco para realizar una resta.\n";
  22. std::cin >> num1;
  23. std::cin >> num2;
  24. resultado = num1 - num2;
  25. std::cout << "El resultado de la resta " << num1 << " - " << num2 << " es = " << resultado << std::endl;
  26.  
  27. Py_RETURN_NONE;
  28. }
  29.  
  30. static PyObject* Operacion_Multiplicacion(PyObject* self, PyObject* args)
  31. {
  32. float num1,num2;
  33. float resultado;
  34. std::cout << "Inserte dos numeros separados por un espacio en blanco para realizar una multiplicacion.\n";
  35. std::cin >> num1;
  36. std::cin >> num2;
  37. resultado = num1 * num2;
  38. std::cout << "El resultado de la multiplicacion " << num1 << " * " << num2 << " es = " << resultado << std::endl;
  39.  
  40. Py_RETURN_NONE;
  41. }
  42. static PyObject* Operacion_RaizCuadrada(PyObject* self, PyObject* args)
  43. {
  44. float num1;
  45. float resultado;
  46. std::cout << "Inserte un numero para calcular la raiz cuadrada.\n";
  47. std::cin >> num1;
  48. resultado = sqrt(num1);
  49. std::cout << "El resultado es = " << resultado;
  50.  
  51. return Py_BuildValue("f",resultado);
  52. }
  53.  

Como observáis, el código comienza importando la librería "Python.h" la que ya incluye dentro de ella librerías como -> limits.h, string.h, stdlib.h, stdio.h, Entre otras, yo también importado "iostream" para hacer el codigo de C++, seguimos y declaramos una función de tipo puntero static PyObject la cual almacenara una dirección en memoria que luego sera llamada desde el Interprete de Python, si queremos pasar argumentos al invocar nuestra función en Python debemos especificar ->
Código
  1. if(!PyArg_ParseTuple(args,"ff",&num1,&num2))
  2. return NULL;
donde "ff" es el tipo de dato, ya sea int, string, char, float, double. (Información mas especifica podéis encontrarla en la documentación de la API), si seguimos observando el código podemos observar la funcion de retorno de tipo
Código
  1. Py_RETURN_NONE
la cual no devolverá nada al finalizar la función. Podemos definir tantas funciones como queramos todas siempre llamadas
Código
  1. static PyObject* NombreFuncion(PyObject* self, PyObject* args)
  2.  
Podemos fijarnos que hay un método con una función que si nos va a devolver un valor al finalizar la función y este es
Código
  1. return Py_BuildValue("f",resultado);
le especificamos la f de flotante y el dato del mismo tipo siempre, que queremos devolver al finalizar la función.

Ya tenemos uno, dos o siete funciones en nuestra librería para Python, ahora lleva la parte de definirlas en una lista, en la cual definiremos un nombre, invocaremos la función, y definiremos una descripción.

Código
  1. static PyMethodDef OperacionMethods[]=
  2. {
  3. {"Suma",Operacion_Suma,METH_VARARGS,"Realiza una suma de dos numeros."},
  4. {"Restar",Operacion_Resta,METH_VARARGS,"Realiza una resta de dos numeros."},
  5. {"Multiplicacion",Operacion_Multiplicacion,METH_VARARGS,"Realiza una multiplicacion de dos numeros."},
  6. {"Raiz",Operacion_RaizCuadrada,METH_VARARGS,"Calcula la raiz cuadrada."},
  7. {NULL,NULL,0,NULL}
  8. };

Si podemos ver la lista, observamos en la primera que hemos dado nombre a la función, ese mismo nombre, "Suma", "Restar", etc... Sera el identificador mediante el cual podremos hacer uso de dicha funcion en Python, seguidamente ingresamos el nombre de la funcion, seguido de METH_VARARGS, y una pequeña descripción, como paso obligatorio para no generar diversos errores en la construcción de nuestro programa deberemos pasarle los parámetros ->
Código
  1. {NULL,NULL,0,NULL}
.

Entonces sabemos que ->
Código
  1. static PyObject* NombreFuncion(PyObject* self, PyObject* args)
  2. {
  3. //Aqui las variables, constantes, estructuras, etc...
  4.        int var;
  5.        struct Persona personas;
  6.  
  7.     if(!PyArg_ParseTuple(args,"i",&var))  //En caso de querer pasar argumentos a la hora de invocar, donde "i" es el tipo de dato y & operador direccion de apunta a la variable en Python.
  8. return NULL;
  9.  
  10. //Aqui todas las instrucciones
  11.  
  12. Py_RETURN_NONE;
  13. }
  14. static PyMethodDef IdentificadorMethods[]=
  15. {
  16. {"IdentificadorParaPython",NombreFuncion,METH_VARARGS,"Realiza una suma de dos numeros."},
  17. {NULL,NULL,0,NULL}
  18. };
  19.  
  20.  
  21.  

Y para finalizar solo quedaría iniciar todas las funciones pasadas por lista, es recomendable utilizar una extensión para funciones que tengan relación entre si, no crear librerías con funciones dedicadas a tareas muy distintas entre si, pues crearíamos confusión al usuario y a nosotros mismo.
Es decir una librería para funciones de flujo, y otra para funciones de operaciones matematicas por ejemplo.

Código
  1. PyMODINIT_FUNC initOperacion(void)
  2. {
  3. (void)Py_InitModule("Operacion",OperacionMethods);
  4. }
  5.  
Aqui iniciamos todas las funciones, vendria a ser el main de un programa sin API.
Código
  1. PyMODINIT_FUNC initOperacion(void)
  2. {
  3. (void)Py_InitModule("NombreParaPython",IdentificadorMethods);
  4. }
  5.  

Con esto tendriamos definida una extension en C o C++, en este caso C++, pero ahora quedaria definirla en un archivo .py para poder construirla y instalarla.
Código
  1. from distutils.core import setup, Extension
  2.  
  3. module = Extension('Identificador',sources = ['modulo.cpp'])
  4.  
  5. setup (name = 'Operaciones',
  6.       version = '1.0',
  7.        author = 'RayBanBOY',
  8.        description = 'Calculadoras',
  9. ext_modules = [module])

Y ya estaria listo para ser usado, simplemente ejecutamos dentro de la carpeta donde se encuentras los dos archivos ->

Código
  1. python setup.py build
  2. python setup.py install
Y como extra os dejo con un script de python que hace uso de esta extension ->
Código
  1. from distutils.core import setup, Extension
  2.  
  3. module = Extension('Operacion',sources = ['modulo.cpp'])
  4.  
  5.  
  6. setup (name = 'Operaciones',
  7.       version = '1.0',
  8.        author = 'RayBanBOY',
  9.        description = 'Calculadoras',
  10. ext_modules = [module])
Código
  1. import Operacion as calc
  2.  
  3. if __name__ == "__main__":
  4.   print('Ingrese un numero para operar.\n')
  5.   print('\t1(Suma), 2(Resta), (3Multiplicacion), 4(Raiz Cuadrada).\n')
  6.   operando = int(input())
  7.  
  8.   if operando == 1:
  9.      calc.Suma()
  10.      print('\n')
  11.   elif operando == 2:
  12.      calc.Restar()
  13.      print('\n')
  14.   elif operando == 3:
  15.      calc.Multiplicacion()
  16.      print('\n')
  17.   elif operando == 4:
  18.      calc.Raiz()
  19.      print('\n')
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  

La documentación y mas detalles lo podéis encontrar en mi canal de YouTube.
Gracias por tu tiempo y ayúdame a seguir mejorando.