No ano passado, houve a necessidade de suplementar um projeto antigo escrito em C com funcionalidade em Python3 . Apesar do fato de haver artigos sobre esse assunto, sofri naquele ano e agora quando escrevi programas para o artigo. Portanto, darei meus exemplos de como trabalhar com Python3 de C no Linux (com o que eu usei). Vou descrever como criar uma classe e chamar seus métodos, para acessar variáveis. Chamando funções e obtendo variáveis do módulo. E também os problemas que encontrei e não consegui entender.
Pacotes
Usamos a API Python padrão para C. Pacotes Python necessários:
- python3
- python3-dev
- python3-all
- python3-all-dev
- libpython3-all-dev
Trabalho no intérprete
A coisa mais simples é carregar e trabalhar no interpretador Python.
Para funcionar, você deve conectar o arquivo de cabeçalho:
#include <Python.h> 
Carregando o intérprete:
 Py_Initialize(); 
A seguir, um bloco de trabalho com o Python, por exemplo:
 PyRun_SimpleString("print('Hello!')"); 
Descarregue o intérprete:
 Py_Finalize(); 
Exemplo completo:
 #include <Python.h> void main() { //   Python Py_Initialize(); //     PyRun_SimpleString("print('Hello!')"); //   Python Py_Finalize(); } 
Como compilar e executar:
 gcc simple.c $(python3-config --includes --ldflags) -o simple && ./simple Hello! 
Mas isso não vai 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 
Isso ocorre porque python3-config --includes --ldflags se expande para esse tipo de coisa:
 -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 
Aqui acho que a ordem de conexão do vinculador -Wl é importante . Quem sabe mais precisamente, escreva sobre isso nos comentários, complementarei a resposta.
Explicação do MooNDeaR :
Tudo é bem simples - os caracteres são pesquisados de uma só vez e todos os caracteres não utilizados são jogados fora. Se você colocar simple.c no final, o vinculador verá o uso do símbolo Py_Initialize () depois de examinar as bibliotecas python, cujos símbolos serão descartados neste momento (porque não foram usados).
Um exemplo de chamada de uma função a partir de um arquivo 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
 
Mas estas são coisas simples e desinteressantes, não obtemos o resultado da função.
Trabalhar com funções e variáveis de módulo
Isso é um pouco mais complicado.
Carregando o interpretador Python e o módulo func.py nele:
 PyObject * python_init() {  
Liberando recursos do interpretador Python:
 void python_clear() {  
Trabalhe com variáveis e funções do módulo.
  char * python_func_get_str(char *val) { char *ret = NULL;  
Vamos nos debruçar sobre isso com mais detalhes.
 pVal = PyObject_CallFunction(pObjct, (char *) "(s)", val); 
"(s)" significa que 1 parâmetro char * é passado como argumento para get_value (x) . Se precisarmos passar vários argumentos para a função, será assim:
 pVal = PyObject_CallFunction(pObjct, (char *) "(sss)", val1, val2, val3); 
Se você precisar passar int , a letra i seria usada, todos os tipos de dados possíveis e suas designações podem ser encontrados na documentação do Python.
 pVal = PyObject_CallFunction(pObjct, (char *) "(i)", my_int); 
func.py:
 
( Problema resolvido )
O problema que encontrei e ainda não conseguia 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; } 
Se eu quiser obter b ou c de func.py , então:
 Py_Finalize(); 
Eu recebo uma falha de segmentação . Não existe esse problema em obter apenas um .
Ao receber variáveis de classe, também não há problemas.
Explicação de pwl :
PyObject PyDict_GetItemString (PyObject p, const char * chave)
Valor de retorno: referência emprestada. Nada precisa ser feito para uma referência emprestada.
O problema era que eu estava chamando Py_XDECREF () para PyDict_GetItemString () . Não é necessário fazer isso para esta função, levando a uma falha de segmentação .
Trabalho de classe
Ainda há um pouco mais complicado.
Carregando o interpretador Python e o módulo class.py nele.
 PyObject * python_init() {  
Passando uma string como argumento e recuperando a string
 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);  
Não houve problemas aqui, tudo funciona sem erros. Existem exemplos na fonte de como trabalhar com int , double , bool .
Enquanto escrevia o material, atualizei meus conhecimentos)
Espero que seja útil.
Referências