Extendendo Python com C

Aqui estou novamente, agora tentando mostrar pra vocês como fazer C conversar com Python. Ao invés de explicar passo a passo como fazer e repetir um monte de documentação que já está no oráculo google, vou simplesmente colar um exemplo com algumas funções e comentários.

Esse exemplo pode servir como template pra o que você está tentando fazer.

Primeiro abra um arquivo chamado avatymodule.c. É convenção sempre usar o nome ‘module’ depois do real nome do módulo. Se você for chamar o módulo de tuxcool, o nome do arquivo deverá ser tuxcoolmodule.c.

Abaixo vai o código do avatymodule.c com alguns comentários:

  1. #include "Python.h"
  2. // Assinatura do nosso modulo
  3. void initavaty(void);
  4. // Função principal que embute o python dentro do C.
  5. main(int argc, char **argv) {
  6. // Inicializa o Interpretador python embutido
  7. Py_Initialize();
  8. // Adiciona o nosso modulo
  9. initavaty();
  10. // Sai do interpretador embutido.
  11. Py_Exit(0);
  12. }
  13. /*
  14. Aqui vão as funções do nosso módulo. Todas que fiz seguiram um modelo.
  15. */
  16. /*
  17. Note que o nome da função sempre vem com [NOME_DO_MODULO]_[NOME_DA_FUNCAO].
  18. Esse metodo apenas da um 'hello world' usando o printf e o print do python.
  19. */
  20. static PyObject *avaty_hello_world(PyObject *self, PyObject* args)
  21. {
  22. printf("[C] Opa, aqui eh C, e usei o printf.\n");
  23. PyRun_SimpleString("import time\n");
  24. PyRun_SimpleString("print '[Py] Aqui eh Python:',time.time()");
  25. printf("\n[C] Aqui é C também.\n");
  26. return Py_None;
  27. }
  28. static PyObject *avaty_argumento_numeros(PyObject *self, PyObject* args)
  29. {
  30. long numero1, numero2, soma;
  31. /* A função PyArg_ParseTuple() é responsável pela transição de argumentos
  32. das funções em python para tipos C.
  33. */
  34. if (!PyArg_ParseTuple(args, "ll:addi", &numero1, &numero2)) {
  35. return NULL;
  36. }
  37. // Note que já trato numero1 e numero2 como long C.
  38. printf("[C] Primeiro Argumento: %d\n",numero1);
  39. printf("[C] Segundo Argumento: %d\n",numero2);
  40. soma = numero1 + numero2;
  41. // Transformando e retornando o resultado da soma em um long Python.
  42. return PyLong_FromUnsignedLong(soma);
  43. }
  44. static PyObject *avaty_argumento_string(PyObject *self, PyObject* args)
  45. {
  46. char *str1, str2[15] = "str_c";
  47. if (!PyArg_ParseTuple(args, "s:addi", &str1)) {
  48. return NULL;
  49. }
  50. printf("[C] Recebido array de caracteres: %s\n",str1);
  51. printf("[C] Contatenacao usando strcat() com: %s\n",str2);
  52. strcat(str2, str1);
  53. printf("[C] Retornando resultado pra objeto string python: %s\n",str2);
  54. return PyString_FromString(str2);
  55. }
  56. /*
  57. Aqui vai uma estrutura com todas as funções dos módulos. Observe que é convenção usar
  58. o nome da função python igual a função C.
  59. */
  60. static PyMethodDef avaty_methods[] = {
  61. {"hello_world",avaty_hello_world,METH_NOARGS, "Documentação do hello world!"},
  62. {"argumento_numeros",avaty_argumento_numeros,METH_VARARGS, "Documentação do argumento_numeros"},
  63. {"argumento_string",avaty_argumento_string,METH_VARARGS, "Documentação do argumento_numeros"},
  64. {NULL, NULL} //sentinela
  65. };
  66. /*
  67. Função para importar o módulo dentro do interpretador embutido, que foi inicializado no main.
  68. */
  69. void initavaty(void)
  70. {
  71. PyImport_AddModule("avaty");
  72. Py_InitModule("avaty", avaty_methods);
  73. }
  74.  

Calma, calma. Eu sei que o highlight ficou uma bosta e você não tá conseguindo entender nada. Coloquei o código aqui no pastebin.com e aqui pra você baixar também.

Agora, tudo que você precisa fazer é gerar um script de build, que nesse exemplo chamarei de buildscript.py (gostou do nome? :) )

flavio@avaty:~/dev/bindings$ cat buildscript.py
from distutils.core import setup, Extension

setup(name=”avaty”, version=”1.0″, ext_modules=[Extension("avaty", ["avatymodule.c"])])

flavio@avaty:~/dev/bindings$

Pronto, agora é só dar:

flavio@avaty:~/dev/bindings$ python buildscript.py build_ext -i
running build_ext
building ‘avaty’ extension
gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions build/temp.linux-i686-2.5/avatymodule.o -o avaty.so
flavio@avaty:~/dev/bindings$ file avaty.so
avaty.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped

Note que um avaty.so vai ter sido criado. Agora é só experimentar se o negócio funcionou mesmo:

flavio@avaty:~/dev/bindings$ python
Python 2.5.2 (r252:60911, Oct  5 2008, 19:24:49)
[GCC 4.3.2] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import avaty
>>> dir(avaty)
['__doc__', '__file__', '__name__', 'argumento_numeros', 'argumento_string', 'hello_world']
>>> avaty.hello_world()
[C] Opa, aqui eh C, e usei o printf.
[Py] Aqui eh Python: 1231955166.34

[C] Aqui é C também.
>>> avaty.argumento_numeros(4,30)
[C] Primeiro Argumento: 4
[C] Segundo Argumento: 30
34L
>>> retorno = avaty.argumento_string(”_to_py”)
[C] Recebido array de caracteres: _to_py
[C] Contatenacao usando strcat() com: str_c
[C] Retornando resultado pra objeto string python: str_c_to_py
>>> print retorno
str_c_to_py
>>> print type(retorno)
<type ’str’>
>>>

Ainda preciso olhar melhor o argumento format da função PyArg_ParseTuple() mas acredito que o conteúdo desse post já serve como pontapé inicial pra muita gente. :)

Pra finalizar, indico a leitura da Python/C API, pra facilitar no uso de funções de transformação de valores C <-> Python.

UPDATE: O link pra download do modulo agora funciona. :)

9 Responses

  1. Fique por dentro Interpretador » Blog Archive » Flávio Ribeiro » Blog Archive » Extendendo Python com C Says:

    [...] nosso modulo. initavaty();. // Sai do interpretador embutido. Py_Exit(0); … fique por dentro clique aqui. Fonte: [...]

  2. Programador Python aprendendo Java | Profissionais TI Says:

    [...] Fonte: http://www.pythonbrasil.com.br/moin.cgi/InicieSe, http://br-linux.org/linux/node/2543, http://pt.wikipedia.org/wiki/Python, http://www.flavioribeiro.com/v2.0/2009/01/14/extendendo-python-com-c/ [...]

  3. Rodrigo Araújo Says:

    Cara, acabei de ler alguns posts seus no Planet, todos muito bons.

    Parabéns pelo conteúdo de qualidade.

  4. Flávio Ribeiro Says:

    Brigado pela visita e o elogio Rodrigo.

    Abraço!

  5. Fábio Cerqueira Says:

    Tentei seguir o tutorial, mas tive problema logo de cara no include, onde fica o Python.h no linux? Precisa baixar algo?

    Ótimo blog. =D

  6. Flávio Ribeiro Says:

    Fala Fábio, esqueci de colocar no ‘tutorial’ que é preciso ter o pacote python-dev (isso se estivermos falando de distribuições baseadas em debian, como o meu ubuntu).

    Enfim, dá um aptitude install python-dev que acho que as coisas vão funcionar.

    Abraço e obrigado pela visita ;)

  7. Fábio Cerqueira Says:

    Brigado Flávio, vou testar aqui depois.

  8. Fábio Cerqueira Says:

    Deu tudo certo aqui Flávio =D
    Vou tentar me aprofundar mais.

  9. Flávio Ribeiro Says:

    Legal Fábio, extensões c\python vão muito mais a fundo do que esse template.. ai eu só fiz transformações de strings e inteiros, a parte carregada fica em cima de instanciar objetos, chamar métodos de uma linguagem em outra e etc.

    Bom estudo!

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.