ctypes - iniciante

100

Tenho a tarefa de "envolver" a biblioteca ac em uma classe Python. Os documentos são incrivelmente vagos sobre esse assunto. Parece que eles esperam que apenas usuários avançados de python implementem ctypes. Bem, eu sou um iniciante em python e preciso de ajuda.

Alguma ajuda passo a passo seria maravilhosa.

Portanto, tenho minha biblioteca c. O que eu faço? Quais arquivos devo colocar onde? Como faço para importar a biblioteca? Eu li que pode haver uma maneira de "quebra automática" para Python.

(A propósito, eu fiz o tutorial ctypes em python.net e não funciona. O que significa que estou pensando que eles estão assumindo que devo ser capaz de preencher o restante das etapas.

Na verdade, este é o erro que recebo com o código deles:

File "importtest.py", line 1
   >>> from ctypes import *
   SyntaxError: invalid syntax

Eu realmente poderia usar alguma ajuda passo a passo nisso! Obrigado ~

Gastak
fonte
10
Você tem o >>> arquivo em importtest.py? Quando as pessoas postam um código que possui >>> em cada linha, isso significa que ele está sendo executado no shell interativo. Para executá-lo a partir de um arquivo, remova >>> (são 3> sinais e um espaço) onde quer que apareça.
Chinmay Kanchi
4
Não digite o >>>s. Eles são impressos pelo shell interativo e devem ser deixados de fora do arquivo de origem.
nmichaels
8
>>>no arquivo .py! OUCH! Nunca vi isso antes!
David Heffernan
3
Honestamente, aprenda um pouco sobre Python (pelo menos um pouco) antes de começar a mexer com ctypes. Você nunca encontrará um tutorial sobre ctypes que presuma que você não conhece o Python básico.
Chinmay Kanchi
3
@spentak: se você pedir ajuda, forneça informações adequadas. Pelo menos nos mostre a última versão do código de que você está falando. O que está na "linha 3", por exemplo?
Francesco

Respostas:

228

Aqui está um tutorial de ctypes rápido e sujo.

Primeiro, escreva sua biblioteca C. Aqui está um exemplo simples do Hello world:

testlib.c

#include <stdio.h>

void myprint(void);

void myprint()
{
    printf("hello world\n");
}

Agora compile-o como uma biblioteca compartilhada ( mac fix encontrada aqui ):

$ gcc -shared -Wl,-soname,testlib -o testlib.so -fPIC testlib.c

# or... for Mac OS X 
$ gcc -shared -Wl,-install_name,testlib.so -o testlib.so -fPIC testlib.c

Em seguida, escreva um wrapper usando ctypes:

testlibwrapper.py

import ctypes

testlib = ctypes.CDLL('/full/path/to/testlib.so')
testlib.myprint()

Agora execute-o:

$ python testlibwrapper.py

E você deve ver a saída

Hello world
$

Se você já tem uma biblioteca em mente, pode pular a parte não Python do tutorial. Certifique-se de que ctypes pode encontrar a biblioteca colocando-a em /usr/libou em outro diretório padrão. Se você fizer isso, não será necessário especificar o caminho completo ao gravar o wrapper. Se você decidir não fazer isso, deverá fornecer o caminho completo da biblioteca ao ligar ctypes.CDLL().

Este não é o lugar para um tutorial mais abrangente, mas se você pedir ajuda com problemas específicos neste site, tenho certeza que a comunidade o ajudará.

PS: Presumo que você esteja no Linux porque já usou ctypes.CDLL('libc.so.6'). Se você estiver em outro sistema operacional, as coisas podem mudar um pouco (ou muito).

Chinmay Kanchi
fonte
1
@ Chinmay: Posso ter um código semelhante para Windows e em vez de C, você poderia fornecer um exemplo visual de c ++? Consigo carregar minha biblioteca, mas não consigo acessar minhas funções a partir do arquivo .dll. Sempre diz "função 'xyz' não encontrada". Você poderia me sugerir uma maneira de contornar isso? Felicidades.
Neófilo
Não sei muito sobre o desenvolvimento do Windows, mas parece que o Windows faz algo errado, talvez ele use uma convenção de chamada diferente? Talvez você deva exportar suas funções C ++ usando "extern C"?
Chinmay Kanchi
Sim, eu fiz isso, mas sem sorte até agora.
Neófilo de
6
Obrigado pelo tutorial fácil de seguir que mostra a funcionalidade básica do ctype
okysabeni
1
rápido e sujo são sempre os melhores tutoriais
lurscher
55

A resposta de Chinmay Kanchi é excelente, mas eu queria um exemplo de função que passa e retorna variáveis ​​/ arrays para um código C ++. Pensei em incluí-lo aqui, caso seja útil para outros.

Passando e retornando um inteiro

O código C ++ para uma função que pega um número inteiro e adiciona um ao valor retornado,

extern "C" int add_one(int i)
{
    return i+1;
}

Salvo como arquivo test.cpp, observe o "C" externo necessário (pode ser removido para o código C). Isso é compilado usando g ++, com argumentos semelhantes à resposta de Chinmay Kanchi,

g++ -shared -o testlib.so -fPIC test.cpp

Os usos de código Python load_libraryda numpy.ctypeslibassumindo que o caminho para a biblioteca compartilhada no mesmo diretório que o script Python,

import numpy.ctypeslib as ctl
import ctypes

libname = 'testlib.so'
libdir = './'
lib=ctl.load_library(libname, libdir)

py_add_one = lib.add_one
py_add_one.argtypes = [ctypes.c_int]
value = 5
results = py_add_one(value)
print(results)

Isso imprime 6 conforme o esperado.

Passando e imprimindo uma matriz

Você também pode passar matrizes da seguinte forma, para um código C imprimir o elemento de uma matriz,

extern "C" void print_array(double* array, int N)
{
    for (int i=0; i<N; i++) 
        cout << i << " " << array[i] << endl;
}

que é compilado como antes e importado da mesma maneira. O código Python extra para usar esta função seria,

import numpy as np

py_print_array = lib.print_array
py_print_array.argtypes = [ctl.ndpointer(np.float64, 
                                         flags='aligned, c_contiguous'), 
                           ctypes.c_int]
A = np.array([1.4,2.6,3.0], dtype=np.float64)
py_print_array(A, 3)

onde especificamos o array, o primeiro argumento para print_array, como um ponteiro para um array Numpy de floats c_contiguous de 64 bits alinhados e o segundo argumento como um inteiro que informa ao código C o número de elementos no array Numpy. Isso então impresso pelo código C da seguinte maneira,

1.4
2.6
3.0
Ed Smith
fonte
5
Esta é uma ótima resposta complementar - pena que não pode haver duas respostas marcadas :(
jtlz2
Não tenho certeza se é muito óbvio, mas há um erro no código. Está faltando import numpy as np. Caso contrário, ele não é capaz de encontrar np.float64e outras coisas.
Ben
@Ben, bom local, adicionado em
Ed Smith
11

Em primeiro lugar: o >>>código que você vê nos exemplos de Python é uma forma de indicar que é um código de Python. É usado para separar o código Python da saída. Como isso:

>>> 4+5
9

Aqui, vemos que a linha que começa com >>>é o código Python e 9 é o que resulta. É exatamente assim que pareceria se você iniciar um interpretador Python, e é por isso que é feito assim.

Você nunca insere a >>>peça em um .pyarquivo.

Isso resolve seu erro de sintaxe.

Em segundo lugar, ctypes é apenas uma das várias maneiras de empacotar bibliotecas Python. Outras formas são SWIG , que examinará sua biblioteca Python e gerará um módulo de extensão Python C que expõe a API C. Outra forma é usar o Cython .

Todos eles têm vantagens e desvantagens.

SWIG irá apenas expor sua API C para Python. Isso significa que você não obtém nenhum objeto ou qualquer coisa, você terá que fazer um arquivo Python separado fazendo isso. No entanto, é comum ter um módulo chamado "wowza" e um módulo SWIG chamado "_wowza" que é o invólucro da API C. Esta é uma maneira fácil e agradável de fazer as coisas.

Cython gera um arquivo C-Extension. Tem a vantagem de que todo o código Python que você escreve é ​​transformado em C, então os objetos que você escreve também estão em C, o que pode ser uma melhoria de desempenho. Mas você terá que aprender como ele faz interface com C, então é um pouco mais de trabalho aprender como usá-lo.

ctypes tem o benefício de não haver código C para compilar, então é muito bom usá-lo para empacotar bibliotecas padrão escritas por outra pessoa e já existe em versões binárias para Windows e OS X.

Lennart Regebro
fonte