FAQ sobre Extensão/Incorporação¶
Posso criar minhas próprias funções em C?¶
Sim, você pode construir módulos embutidos contendo funções, variáveis, exceções e até mesmo novos tipos em C. Isso é explicado no documento Estendendo e Incorporando o Interpretador Python.
A maioria dos livros intermediários ou avançados em Python também abordará esse tópico.
Posso criar minhas próprias funções em C++?¶
Sim, usando recursos de compatibilidade encontrados em C++. Coloque extern "C" { ... }
em torno dos arquivos de inclusão do Python e coloque extern "C"
antes de cada função que será chamada pelo interpretador do Python. Objetos globais ou estáticos em C++ com construtores provavelmente não são uma boa ideia.
Escrever C é difícil; há alguma alternativa?¶
Há um número de alternativas para escrever suas próprias extensões em C, dependendo daquilo que você está tentando fazer.
O Cython e o seu parente Pyrex são compiladores que aceitam uma forma de Python levemente modificada e geram o código C correspondente. Cython e Pyrex possibilitam escrever extensões sem que seja necessário aprender a usar a API C do Python.
Se você precisar interagir com alguma biblioteca C ou C++ que não é suportada por nenhuma extensão do Python no momento, você pode tentar envolver os tipos de dados e funções da biblioteca usando uma ferramenta como o SWIG. SIP, CXX Boost, e Weave são outras alternativas para criar invólucros de bibliotecas C++.
Como posso executar instruções arbitrárias de Python a partir de C?¶
A função mais alto-nível para isso é a PyRun_SimpleString()
, que recebe como único argumento uma string a ser executada no contexto do módulo __main__
e retorna 0
para sucesso e -1
quando uma exceção ocorrer (incluindo SyntaxError
). Se quiser mais controle, use PyRun_String()
; veja o código-fonte de PyRun_SimpleString()
em Python/pythonrun.c
.
Como posso executar e obter o resultado de uma expressão Python arbitrária a partir de C?¶
Chame a função PyRun_String()
da pergunta anterior passando Py_eval_input
como o símbolo de início; ela faz a análise sintática de uma expressão, a executa, e retorna o seu valor.
Como extraio valores em C a partir de um objeto Python?¶
Depende do tipo do objeto. Se for uma tupla, a PyTuple_Size()
retorna o seu comprimento e a PyTuple_GetItem()
retorna o item em um determinado índice. Listas têm funções similares, PyList_Size()
e PyList_GetItem()
.
Para bytes, a PyBytes_Size()
retorna o comprimento e a PyBytes_AsStringAndSize()
fornece um ponteiro para o seu valor e o seu comprimento. Note que objetos bytes em Python podem conter bytes nulos, de forma que a strlen()
do C não deve ser usada.
Para testar o tipo de um objeto, primeiramente se certifique de que ele não é NULL
, e então use PyBytes_Check()
, PyTuple_Check()
, PyList_Check()
, etc.
Também existe uma API alto-nível para objetos Python fornecida pela chamada interface “abstrata” – leia Include/abstract.h
para mais detalhes. Ela permite interagir com qualquer tipo de sequência Python usando chamadas como PySequence_Length()
, PySequence_GetItem()
, etc, além de vários outros protocolos úteis tais como números (PyNumber_Index()
e outros) e mapeamentos nas APIs PyMapping.
Como posso utilizar Py_BuildValue() para criar uma tupla de comprimento arbitrário?¶
Não é possível. Use a função PyTuple_Pack()
para isso.
Como eu chamo um método de um objeto a partir do C?¶
A função PyObject_CallMethod()
pode ser usada para chamar um método arbitrário de um objeto. Os parâmetros são o objeto, o nome do método a ser chamado, uma string de formato como a usada em Py_BuildValue()
, e os valores dos argumentos:
PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
const char *arg_format, ...);
Isso funciona para qualquer objeto que tenha métodos – sejam eles embutidos ou definidos por usuário. Você fica então responsável por chamar Py_DECREF()
no valor de retorno.
Para chamar, por exemplo, o método “seek” de um objeto arquivo com argumentos 10, 0 (presumindo que “f” é o ponteiro para o objeto arquivo):
res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
... ocorreu uma exceção ...
}
else {
Py_DECREF(res);
}
Note que a função PyObject_CallObject()
sempre recebe os argumentos da chamada como uma tupla, de forma que para chamar uma função sem argumentos deve-se passar “()” como formato, e para chamar uma função com 1 argumento, coloque-o entre parênteses, por exemplo “(i)”.
Como posso capturar a saída da função PyErr_Print() (ou qualquer outra coisa que escreva para stdout/stderr)?¶
Com código Python, defina um objeto que suporte o método write()
. Atribua esse objeto a sys.stdout
e sys.stderr
. Chame print_error, or simplesmente deixe o mecanismo padrão de traceback acontecer. Assim, a saída irá para onde quer que o seu método write()
a envie.
O jeito mais fácil de fazer isso é usar a classe io.StringIO
:
>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!
Um objeto personalizado para fazer a mesma coisa seria esse:
>>> import io, sys
>>> class StdoutCatcher(io.TextIOBase):
... def __init__(self):
... self.data = []
... def write(self, stuff):
... self.data.append(stuff)
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(''.join(sys.stdout.data))
foo
hello world!
Como faço para acessar a partir do C um módulo escrito em Python?¶
Você pode obter um pointeiro para o objeto de módulo da seguinte maneira:
module = PyImport_ImportModule("<modulename>");
Se o módulo ainda não foi importado (isto é, ainda não aparece no sys.modules
), essa função vai inicializar o módulo; caso contrário, ela vai simplesmente retornar o valor de sys.modules["<modulename>"]
. Note que ela não adiciona o módulo a nenhum espaço de nomes – ela simplesmente garante que ele foi inicializado e colocado no sys.modules
.
Você pode então acessar os atributos do módulo (isto é, qualquer nome definido no módulo) assim:
attr = PyObject_GetAttrString(module, "<attrname>");
Chamar PyObject_SetAttrString()
para definir variáveis no módulo também funciona.
Como posso interagir com objetos C++ a partir do Python?¶
Dependendo das suas necessidades, há diversas abordagens. Para fazer isso manualmente, comece lendo o documento “Estendendo e Incorporando”. Note que, para o sistema Python em tempo de execução, não há muita diferença entre C e C++ – de forma que a estratégia de construir um novo tipo Python ao redor de uma estrutura C (ou ponteiro para uma) também funciona para objetos C++.
Para bibliotecas C++, veja Escrever C é difícil; há alguma alternativa?.
Adicionei um módulo usando o arquivo de Setup e o make falha; por quê?¶
O Setup deve terminar com uma quebra de linha; se não houver uma quebra de linha no final, o processo de compilação falha. (Consertar isso requer umas gambiarras feias em shell script, e esse bug é tão pequeno que o esforço não parece valer a pena.)
Como eu depuro uma extensão?¶
Ao usar o GDB com extensões carregadas dinamicamente, você não consegue definir um ponto de interrupção antes da sua extensão ser carregada.
No seu arquivo .gdbinit
(ou então interativamente), adicione o comando:
br _PyImport_LoadDynamicModule
Então, ao executar o GDB:
$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # repita até que a sua extensão seja carregada
gdb) finish # para que a sua extensão seja carregada
gdb) br myfunction.c:50
gdb) continue
Quero compilar um módulo Python no meu sistema Linux, mas alguns arquivos estão faltando. Por quê?¶
A maioria das versões empacotadas de Python omitem alguns arquivos necessários para compilar extensões do Python.
Em sistemas Red Hat, instale o RPM python3-devel para obter os arquivos necessários.
Para Debian, execute apt-get install python3-dev
.
Como posso distinguir “entrada incompleta” de “entrada inválida”?¶
Às vezes você quer emular o comportamento do interpretador interativo do Python, que te dá um prompt de continuação quando a entrada está incompleta (por exemplo, você digitou o início de uma instrução “if”, ou então não fechou os parênteses ou aspas triplas), mas que te dá um mensagem de erro de sintaxe imediatamente se a entrada for inválida.
Em Python você pode usar o módulo codeop
, que aproxima suficientemente o comportamento do analisador sintático. Por exemplo, o IDLE o usa.
Em C, a forma mais fácil de fazer isso é chamar PyRun_InteractiveLoop()
(talvez em uma thread separada) e deixar o interpretador do Python tratar a entrada para você. Você tambémm pode apontar o PyOS_ReadlineFunctionPointer()
para a sua função de entrada personalizada. Consulte Modules/readline.c
e Parser/myreadline.c
para mais dicas.
Como encontro os símbolos __builtin_new ou __pure_virtual não-definidos no g++?¶
Para carregar dinamicamente módulos de extensão feitos com g++, você precisa recompilar o Python, usando o g++ como ligador (mude a constante LINKCC no Makefile dos módulos de extensão do Python), e use o g++ também como ligador do seu módulo (por exemplo, g++ -shared -o mymodule.so mymodule.o
).
Posso criar uma classe de objetos com alguns métodos implementados em C e outros em Python (por exemplo, via herança)?¶
Sim, você pode herdar de classes embutidas como int
, list
, dict
etc.
A Boost Python Library (BPL, https://www.boost.org/libs/python/doc/index.html) fornece uma forma de fazer isso a partir do C++ (quer dizer, você consegue herdar de uma classe de extensão escrita em C++ usando a BPL).