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