Python - Programação de extensão com C
Qualquer código que você escrever usando qualquer linguagem compilada como C, C++ ou Java pode ser integrado ou importado em outro script Python. Este código é considerado como uma "extensão".
Um módulo de extensão Python nada mais é do que uma biblioteca C normal. Em máquinas Unix, essas bibliotecas geralmente terminam em .so (para objeto compartilhado). Em máquinas Windows, normalmente você vê .dll (para biblioteca vinculada dinamicamente).
Pré-requisitos para escrever extensões
Para começar a escrever sua extensão, você precisará dos arquivos de cabeçalho do Python.
-
Em máquinas Unix, isso geralmente requer a instalação de um pacote específico do desenvolvedor, como python2.5-dev.
-
Os usuários do Windows obtêm esses cabeçalhos como parte do pacote quando usam o instalador binário do Python.
Além disso, supõe-se que você tenha um bom conhecimento de C ou C++ para escrever qualquer extensão Python usando programação C.
Veja primeiro uma extensão Python
Para sua primeira olhada em um módulo de extensão Python, você precisa agrupar seu código em quatro partes -
-
O arquivo de cabeçalho Python.h .
-
As funções C que você deseja expor como a interface do seu módulo.
-
Uma tabela mapeando os nomes de suas funções conforme os desenvolvedores do Python as veem para funções C dentro do módulo de extensão.
-
Uma função de inicialização.
O arquivo de cabeçalho Python.h
Você precisa incluir Python.h header em seu arquivo de origem C, que fornece acesso à API interna do Python usada para conectar seu módulo ao interpretador.
Certifique-se de incluir Python.h antes de quaisquer outros cabeçalhos que você possa precisar. Você precisa seguir as inclusões com as funções que deseja chamar do Python.
As funções C
As assinaturas da implementação C de suas funções sempre assumem uma das três formas a seguir -
static PyObject *MyFunction( PyObject *self, PyObject *args ); static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw); static PyObject *MyFunctionWithNoArgs( PyObject *self );
Cada uma das declarações anteriores retorna um objeto Python. Não existe vazio função em Python como existe em C. Se você não quiser que suas funções retornem um valor, retorne o equivalente em C de Nenhum do Python valor. Os cabeçalhos do Python definem uma macro, Py_RETURN_NONE, que faz isso para nós.
Os nomes de suas funções C podem ser o que você quiser, pois nunca são vistos fora do módulo de extensão. Eles são definidos como estático função.
Suas funções C geralmente são nomeadas combinando o módulo Python e os nomes da função, como mostrado aqui −
static PyObject *module_func(PyObject *self, PyObject *args) { /* Do your stuff here. */ Py_RETURN_NONE; }
Esta é uma função Python chamada func dentro do módulo módulo . Você colocará ponteiros para suas funções C na tabela de métodos para o módulo que geralmente vem a seguir em seu código-fonte.
A Tabela de Mapeamento de Métodos
Esta tabela de métodos é uma matriz simples de estruturas PyMethodDef. Essa estrutura se parece com isso -
struct PyMethodDef { char *ml_name; PyCFunction ml_meth; int ml_flags; char *ml_doc; };
Aqui está a descrição dos membros desta estrutura -
-
ml_name − Este é o nome da função que o interpretador Python apresenta quando é usado em programas Python.
-
ml_meth − Este deve ser o endereço de uma função que tenha qualquer uma das assinaturas descritas na seção anterior.
-
ml_flags − Isso informa ao interpretador qual das três assinaturas ml_meth está usando.
-
Esse sinalizador geralmente tem um valor de METH_VARARGS.
-
Este sinalizador pode ser OU bit a bit com METH_KEYWORDS se você quiser permitir argumentos de palavras-chave em sua função.
-
Isso também pode ter um valor de METH_NOARGS que indica que você não deseja aceitar nenhum argumento.
-
-
ml_doc − Esta é a docstring para a função, que pode ser NULL se você não quiser escrever uma.
Essa tabela precisa ser encerrada com um sentinela que consiste em valores NULL e 0 para os membros apropriados.
Exemplo
Para a função definida acima, temos a seguinte tabela de mapeamento de métodos -
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { NULL, NULL, 0, NULL } };
A Função de Inicialização
A última parte do seu módulo de extensão é a função de inicialização. Esta função é chamada pelo interpretador Python quando o módulo é carregado. É necessário que a função seja denominada initMódulo , onde Módulo é o nome do módulo.
A função de inicialização precisa ser exportada da biblioteca que você construirá. Os cabeçalhos do Python definem PyMODINIT_FUNC para incluir os encantamentos apropriados para que isso aconteça para o ambiente específico em que estamos compilando. Tudo o que você precisa fazer é usá-lo ao definir a função.
Sua função de inicialização C geralmente tem a seguinte estrutura geral -
PyMODINIT_FUNC initModule() { Py_InitModule3(func, module_methods, "docstring..."); }
Aqui está a descrição de Py_InitModule3 função -
-
função − Esta é a função a ser exportada.
-
módulo _métodos − Este é o nome da tabela de mapeamento definido acima.
-
docstring − Este é o comentário que você quer dar no seu ramal.
Juntando tudo isso se parece com o seguinte -
#include <Python.h> static PyObject *module_func(PyObject *self, PyObject *args) { /* Do your stuff here. */ Py_RETURN_NONE; } static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { NULL, NULL, 0, NULL } }; PyMODINIT_FUNC initModule() { Py_InitModule3(func, module_methods, "docstring..."); }
Exemplo
Um exemplo simples que faz uso de todos os conceitos acima -
#include <Python.h> static PyObject* helloworld(PyObject* self) { return Py_BuildValue("s", "Hello, Python extensions!!"); } static char helloworld_docs[] = "helloworld( ): Any message you want to put here!!\n"; static PyMethodDef helloworld_funcs[] = { {"helloworld", (PyCFunction)helloworld, METH_NOARGS, helloworld_docs}, {NULL} }; void inithelloworld(void) { Py_InitModule3("helloworld", helloworld_funcs, "Extension module example!"); }
Aqui o Py_BuildValue A função é usada para construir um valor Python. Salve o código acima no arquivo hello.c. Veríamos como compilar e instalar este módulo para ser chamado a partir do script Python.
Criação e instalação de extensões
Os distutils O pacote facilita muito a distribuição de módulos Python, tanto Python puro quanto módulos de extensão, de maneira padrão. Os módulos são distribuídos na forma de fonte e construídos e instalados por meio de um script de configuração geralmente chamado setup.py do seguinte modo.
Para o módulo acima, você precisa preparar o seguinte script setup.py -
from distutils.core import setup, Extension setup(name='helloworld', version='1.0', \ ext_modules=[Extension('helloworld', ['hello.c'])])
Agora, use o seguinte comando, que executaria todas as etapas de compilação e vinculação necessárias, com os comandos e sinalizadores corretos do compilador e do vinculador, e copie a biblioteca dinâmica resultante em um diretório apropriado -
$ python setup.py install
Em sistemas baseados em Unix, você provavelmente precisará executar este comando como root para ter permissões para gravar no diretório de pacotes do site. Isso geralmente não é um problema no Windows.
Importando extensões
Depois de instalar sua extensão, você poderá importar e chamar essa extensão em seu script Python da seguinte maneira -
#!/usr/bin/python import helloworld print helloworld.helloworld()
Isso produziria o seguinte resultado -
Hello, Python extensions!!
Passando parâmetros de função
Como você provavelmente desejará definir funções que aceitem argumentos, você pode usar uma das outras assinaturas para suas funções C. Por exemplo, a seguinte função, que aceita algum número de parâmetros, seria definida assim −
static PyObject *module_func(PyObject *self, PyObject *args) { /* Parse args and do something interesting here. */ Py_RETURN_NONE; }
A tabela de métodos contendo uma entrada para a nova função ficaria assim −
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { "func", module_func, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } };
Você pode usar a API PyArg_ParseTuple para extrair os argumentos de um ponteiro PyObject passado para sua função C.
O primeiro argumento para PyArg_ParseTuple é o argumento args. Este é o objeto que você analisará . O segundo argumento é uma string de formato que descreve os argumentos como você espera que eles apareçam. Cada argumento é representado por um ou mais caracteres na string de formato da seguinte forma.
static PyObject *module_func(PyObject *self, PyObject *args) { int i; double d; char *s; if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) { return NULL; } /* Do something interesting here. */ Py_RETURN_NONE; }
Compilar a nova versão do seu módulo e importá-la permite que você invoque a nova função com qualquer número de argumentos de qualquer tipo −
module.func(1, s="three", d=2.0) module.func(i=1, d=2.0, s="three") module.func(s="three", d=2.0, i=1)
Você provavelmente pode criar ainda mais variações.
A PyArg_ParseTuple Função
Aqui está a assinatura padrão para PyArg_ParseTuple função -
int PyArg_ParseTuple(PyObject* tuple,char* format,...)
Esta função retorna 0 para erros e um valor diferente de 0 para sucesso. tupla é o PyObject* que foi o segundo argumento da função C. Aqui formato é uma string C que descreve argumentos obrigatórios e opcionais.
Aqui está uma lista de códigos de formato para PyArg_ParseTuple função -
Código | tipo C | Significado |
---|---|---|
c | caractere | Uma string Python de comprimento 1 se torna um caractere C. |
d | duplo | Um float Python se torna um duplo C. |
f | flutuar | Um float Python se torna um float C. |
i | int | Um int Python se torna um int C. |
l | longo | Um int do Python se torna um C long. |
L | longo longo | Um int do Python se torna um C long |
O | PyObject* | Obtém referência não NULL emprestada ao argumento Python. |
s | caractere* | String Python sem nulos incorporados a C char*. |
s# | caracter*+int | Qualquer string Python para endereço e comprimento C. |
t# | caracter*+int | Buffer de segmento único somente leitura para endereço e comprimento C. |
u | Py_UNICODE* | Python Unicode sem nulos incorporados em C. |
u# | Py_UNICODE*+int | Qualquer endereço e comprimento Python Unicode C. |
w# | caracter*+int | Ler/gravar buffer de segmento único no endereço e comprimento C. |
z | caractere* | Assim como s, também aceita None (configura C char* como NULL). |
z# | caracter*+int | Como s#, também aceita None (configura C char* como NULL). |
(...) | conforme ... | Uma sequência Python é tratada como um argumento por item. |
| | Os argumentos a seguir são opcionais. | |
: | Fim do formato, seguido do nome da função para mensagens de erro. | |
; | Fim do formato, seguido por todo o texto da mensagem de erro. |
Valores de retorno
Py_BuildValue recebe uma string de formato muito parecida com PyArg_ParseTuple faz. Em vez de passar os endereços dos valores que você está construindo, você passa os valores reais. Aqui está um exemplo mostrando como implementar uma função add -
static PyObject *foo_add(PyObject *self, PyObject *args) { int a; int b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } return Py_BuildValue("i", a + b); }
É assim que ficaria se implementado em Python -
def add(a, b): return (a + b)
Você pode retornar dois valores de sua função da seguinte forma, isso seria capturado usando uma lista em Python.
static PyObject *foo_add_subtract(PyObject *self, PyObject *args) { int a; int b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } return Py_BuildValue("ii", a + b, a - b); }
É assim que ficaria se implementado em Python -
def add_subtract(a, b): return (a + b, a - b)
O Py_BuildValue Função
Aqui está a assinatura padrão para Py_BuildValue função -
PyObject* Py_BuildValue(char* format,...)
Aqui formato é uma string C que descreve o objeto Python a ser construído. Os seguintes argumentos de Py_BuildValue são valores C a partir dos quais o resultado é construído. O PyObject* resultado é uma nova referência.
A tabela a seguir lista as strings de código comumente usadas, das quais zero ou mais são unidas no formato de string.
Código | tipo C | Significado |
---|---|---|
c | caractere | Um caractere C se torna uma string Python de comprimento 1. |
d | duplo | Um duplo C se torna um float Python. |
f | flutuar | Um float C se torna um float Python. |
i | int | Um int C se torna um int Python. |
l | longo | Um C long se torna um Python int. |
N | PyObject* | Passa um objeto Python e rouba uma referência. |
O | PyObject* | Passa um objeto Python e o INCREF normalmente. |
O& | converter+void* | Conversão arbitrária |
s | caractere* | C 0-terminated char* para string Python ou NULL para None. |
s# | caracter*+int | C char* e length para string Python ou NULL para None. |
u | Py_UNICODE* | String terminada em C-wide para Python Unicode ou NULL para None. |
u# | Py_UNICODE*+int | String de C-wide e comprimento para Python Unicode ou NULL para None. |
w# | caracter*+int | Ler/gravar buffer de segmento único no endereço e comprimento C. |
z | caractere* | Assim como s, também aceita None (configura C char* como NULL). |
z# | caracter*+int | Como s#, também aceita None (configura C char* como NULL). |
(...) | conforme ... | Cria tupla Python a partir de valores C. |
[...] | conforme ... | Cria uma lista Python a partir de valores C. |
{...} | conforme ... | Cria dicionário Python a partir de valores C, alternando chaves e valores. |
Code {...} constrói dicionários a partir de um número par de valores C, alternadamente chaves e valores. Por exemplo, Py_BuildValue("{issi}",23,"zig","zag",42) retorna um dicionário como o {23:'zig','zag':42} do Python.
python
- Programação Orientada a Objetos Python
- Instrução Python Print():Como imprimir com exemplos
- Função Python String strip () com EXEMPLO
- Python String count() com EXEMPLOS
- Python String format() Explique com EXEMPLOS
- Método Python String find() com exemplos
- Funções do Python Lambda com EXEMPLOS
- Função Python round() com EXEMPLOS
- Função Python map() com EXEMPLOS
- Python Timeit() com exemplos