El año pasado, era necesario complementar un antiguo proyecto escrito en C con funcionalidad en Python3 . A pesar del hecho de que hay artículos sobre este tema, sufrí en ese año y ahora cuando escribí programas para el artículo. Por lo tanto, daré mis ejemplos sobre cómo trabajar con Python3 desde C en Linux (con lo que usé). Describiré cómo crear una clase y llamar a sus métodos, para acceder a las variables. Llamando funciones y obteniendo variables del módulo. Y también los problemas que encontré y no pude entender.
Paquetes
Utilizamos la API estándar de Python para C. Paquetes necesarios de Python:
- python3
- python3-dev
- python3-all
- python3-all-dev
- libpython3-all-dev
Trabajar en el intérprete
Lo más simple es cargar y trabajar en el intérprete de Python.
Para trabajar, debe conectar el archivo de encabezado:
#include <Python.h>
Cargando el intérprete:
Py_Initialize();
El siguiente es un bloque de trabajo con Python, por ejemplo:
PyRun_SimpleString("print('Hello!')");
Descargue el intérprete:
Py_Finalize();
Ejemplo completo:
#include <Python.h> void main() { // Python Py_Initialize(); // PyRun_SimpleString("print('Hello!')"); // Python Py_Finalize(); }
Cómo compilar y ejecutar:
gcc simple.c $(python3-config --includes --ldflags) -o simple && ./simple Hello!
Pero esto no funcionará:
gcc $(python3-config --includes --ldflags) simple.c -o simple && ./simple /tmp/ccUkmq57.o: In function `main': simple.c:(.text+0x5): undefined reference to `Py_Initialize' simple.c:(.text+0x16): undefined reference to `PyRun_SimpleStringFlags' simple.c:(.text+0x1b): undefined reference to `Py_Finalize' collect2: error: ld returned 1 exit status
Esto se debe a que python3-config --include --ldflags se expande en este tipo de cosas:
-I/usr/include/python3.6m -I/usr/include/python3.6m -L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.6m -lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
Aquí creo que el orden de conexión del enlazador -Wl es importante . Quién sabe más precisamente, escriba sobre esto en los comentarios, complementaré la respuesta.
Explicación de MooNDeaR :
Todo es bastante simple: los personajes se buscan en una sola pasada y todos los caracteres no utilizados se desechan. Si pone simple.c al final, resulta que el enlazador verá el uso del símbolo Py_Initialize () después de mirar las bibliotecas de Python, todos los símbolos de los cuales serán descartados en este momento (porque no se usaron).
Un ejemplo de llamar a una función desde un archivo Python:
simple.c
#include <Python.h> void python() { // Python Py_Initialize(); // // sys PyRun_SimpleString("import sys"); // python PyRun_SimpleString("sys.path.append('./src/python')"); PyRun_SimpleString("import simple"); PyRun_SimpleString("print(simple.get_value(2))"); PyRun_SimpleString("print(simple.get_value(2.0))"); PyRun_SimpleString("print(simple.get_value(\"Hello!\"))"); // Python Py_Finalize(); } void main() { puts("Test simple:"); python(); }
simple.py
Pero estas son cosas simples y poco interesantes, no obtenemos el resultado de la función.
Trabajar con funciones y variables de módulo.
Esto es un poco más complicado.
Cargando el intérprete de Python y el módulo func.py en él:
PyObject * python_init() {
Liberación de recursos de intérpretes de Python:
void python_clear() {
Trabajar con variables y funciones de módulos.
char * python_func_get_str(char *val) { char *ret = NULL;
Detengámonos en esto con más detalle.
pVal = PyObject_CallFunction(pObjct, (char *) "(s)", val);
"(s)" significa que 1 parámetro char * se pasa como argumento para obtener_valor (x) . Si necesitamos pasar varios argumentos a la función, será así:
pVal = PyObject_CallFunction(pObjct, (char *) "(sss)", val1, val2, val3);
Si necesita pasar int , entonces se usaría la letra i , todos los tipos de datos posibles y sus designaciones se pueden encontrar en la documentación de Python.
pVal = PyObject_CallFunction(pObjct, (char *) "(i)", my_int);
func.py:
( Problema resuelto )
El problema que encontré y que aún no podía entender:
int main() { puts("Test func:"); if (!python_init()) { puts("python_init error"); return -1; } puts("Strings:"); printf("\tString: %s\n", python_func_get_str("Hello from Python!")); puts("Attrs:"); printf("\ta: %d\n", python_func_get_val("a")); printf("\tb: %d\n", python_func_get_val("b")); printf("\tc: %d\n", python_func_get_val("c")); python_clear(); return 0; }
Si quiero obtener b o c de func.py , entonces en:
Py_Finalize();
Me sale un error de segmentación . No hay tal problema con obtener solo un .
Al recibir variables de clase, tampoco hay problemas.
Explicación de pwl :
PyObject PyDict_GetItemString (PyObject p, const char * key)
Valor de retorno: referencia prestada. No es necesario hacer nada para obtener una referencia prestada.
El problema era que estaba llamando a Py_XDECREF () para PyDict_GetItemString () . No es necesario hacer esto para esta función, lo que lleva a una falla de segmentación .
Trabajo en clase
Todavía hay un poco más complicado.
Cargando el intérprete de Python y el módulo class.py en él.
PyObject * python_init() {
Pasar una cadena como argumento y recuperar la cadena
char * python_class_get_str(char *val) { char *ret = NULL; pVal = PyObject_CallMethod(pInstance, (char *) "get_value", (char *) "(s)", val); if (pVal != NULL) { PyObject* pResultRepr = PyObject_Repr(pVal);
Aquí no hubo problemas, todo funciona sin errores. Hay ejemplos en la fuente de cómo trabajar con int , double , bool .
Mientras escribía material, actualicé mis conocimientos)
Espero que te sea útil.
Referencias