Pergunta

Há algum tempo venho pensando em escrever uma biblioteca adequada.Conheço Python bastante bem e pretendo implementar tudo em Python para começar, mas imagino que eventualmente terei que reimplementar algumas rotinas básicas em uma linguagem compilada.

IIRC, uma das atribuições originais do Python era como linguagem de prototipagem, no entanto, o Python é bastante liberal ao permitir que funções, functores e objetos sejam passados ​​​​para funções e métodos, embora eu suspeito que o mesmo não seja verdade, digamos, C ou Fortran.

O que devo saber sobre o design de funções/classes que imagino que terão que fazer interface com a linguagem compilada?E quanto desses problemas potenciais são tratados por bibliotecas como cTypes, bgen, GOLE, Boost.Python, Cython ou SIP Python?

Para este caso de uso específico, (uma biblioteca adequada), imagino permitir que os usuários definam funções matemáticas (Guassian, Lorentzian etc.) como funções Python que podem então ser passadas e interpretadas pela biblioteca de ajuste de código compilado.Passar e retornar arrays também é essencial.

Foi útil?

Solução

Finalmente uma pergunta à qual posso realmente dar uma resposta valiosa :).

Investiguei f2py, boost.python, swig, cython e pyrex para meu trabalho (PhD em técnicas de medição óptica).Usei swig extensivamente, boost.python alguns e pyrex e cython muito.Eu também usei ctypes.Esta é a minha análise:

Isenção de responsabilidade:Está é minha experiência pessoal.Não estou envolvido em nenhum desses projetos.

gole:não funciona bem com c++.Deveria, mas problemas de confusão de nomes na etapa de vinculação foram uma grande dor de cabeça para mim no Linux e no Mac OS X.Se você possui código C e deseja fazer interface com python, é uma boa solução.Eu preparei o GTS para minhas necessidades e precisei escrever basicamente uma biblioteca C compartilhada à qual eu pudesse me conectar.Eu não recomendaria.

Tipos:Eu escrevi um wrapper libdc1394 (biblioteca de câmeras IEEE) usando ctypes e foi uma experiência muito simples.Você pode encontrar o código em https://launchpad.net/pydc1394.É muito trabalhoso converter cabeçalhos em código python, mas tudo funciona de maneira confiável.Esta é uma boa maneira se você deseja fazer interface com uma biblioteca externa.Ctypes também está no stdlib do python, para que todos possam usar seu código imediatamente.Essa também é uma boa maneira de brincar rapidamente com uma nova biblioteca em python.Posso recomendá-lo para fazer interface com bibliotecas externas.

Boost.Python:Muito agradável.Se você já possui seu próprio código C++ que deseja usar em python, vá em frente.É muito fácil traduzir estruturas de classe C++ em estruturas de classe Python dessa maneira.Eu o recomendo se você tiver o código c++ necessário em python.

Pirex/Cython: Use Cython, não Pyrex.Período.Cython é mais avançado e mais agradável de usar.Hoje em dia faço com cython tudo o que fazia com SWIG ou Ctypes.Também é a melhor maneira se você tiver um código python muito lento.O processo é absolutamente fantástico:você converte seus módulos python em módulos cython, constrói-os e continua criando perfis e otimizando como se ainda fosse python (nenhuma mudança de ferramentas necessária).Você pode então aplicar tanto (ou pouco) código C misturado com seu código python.Isso é muito mais rápido do que reescrever partes inteiras do seu aplicativo em C;você apenas reescreve o loop interno.

Horários:ctypes tem a maior sobrecarga de chamada (~700ns), seguido por boost.python (322ns) e diretamente por swig (290ns).Cython tem a menor sobrecarga de chamada (124ns) e o melhor feedback onde passa o tempo (suporte cProfile!).Os números são da minha caixa chamando uma função trivial que retorna um número inteiro de um shell interativo;a sobrecarga de importação do módulo não é, portanto, cronometrada, apenas a sobrecarga da chamada de função é.Portanto, é mais fácil e produtivo obter código python rapidamente criando perfis e usando cython.

Resumo:Para o seu problema, use Cython;).Espero que este resumo seja útil para algumas pessoas.Terei prazer em responder a qualquer pergunta restante.


Editar:Esqueci de mencionar:para fins numéricos (ou seja, conexão com NumPy) use Cython;eles têm suporte para isso (porque basicamente desenvolvem o cython para esse fim).Portanto, este deve ser outro +1 para sua decisão.

Outras dicas

Não usei SWIG ou SIP, mas acho que escrever wrappers Python com impulsionar.python ser muito poderoso e relativamente fácil de usar.

Não estou claro quais são seus requisitos para passar tipos entre C/C++ e python, mas você pode fazer isso facilmente expondo um tipo C++ ao python ou usando um genérico impulso::python::objeto argumento para sua API C++.Você também pode registrar conversores para converter automaticamente tipos python em tipos C++ e vice-versa.

Se você planeja usar boost.python, o tutorial é um bom lugar para começar.

Eu implementei algo semelhante ao que você precisa.Eu tenho uma função C que aceita uma função python e uma imagem como argumentos, e aplica a função python a cada pixel na imagem.

Image* unary(boost::python::object op, Image& im)
{
    Image* out = new Image(im.width(), im.height(), im.channels());
    for(unsigned int i=0; i<im.size(); i++)
    {
        (*out)[i] == extract<float>(op(im[i]));
    }
    return out;
}

Nesse caso, Image é um objeto C++ exposto a python (uma imagem com pixels flutuantes) e op é uma função definida por python (ou realmente qualquer objeto python com um atributo __call__).Você pode então usar esta função da seguinte maneira (assumindo que o unário está localizado na imagem chamada que também contém Image e uma função de carregamento):

import image
im = image.load('somefile.tiff')
double_im = image.unary(lambda x: 2.0*x, im)

Quanto ao uso de arrays com boost, eu pessoalmente não fiz isso, mas sei que a funcionalidade para expor arrays a python usando boost está disponível - esse pode ser útil.

A melhor maneira de planejar uma eventual transição para código compilado é escrever as partes sensíveis ao desempenho como um módulo de funções simples em um estilo funcional (sem estado e sem efeitos colaterais), que aceitam e retornam tipos de dados básicos.

Isso fornecerá um mapeamento individual do código do protótipo Python para o eventual código compilado e permitirá que você use tipos facilmente e evite um monte de dores de cabeça.

Para ajuste de pico, você quase certamente precisará usar arrays, o que complicará um pouco as coisas, mas ainda é muito viável com ctypes.

Se você realmente deseja usar estruturas de dados mais complicadas ou modificar os argumentos passados, GOLE ou Interface de extensão C padrão do Python permitirá que você faça o que quiser, mas com algum incômodo.

Para o que você está fazendo, você também pode conferir NumPy, que pode fazer parte do trabalho que você gostaria de enviar para C, além de oferecer alguma ajuda adicional na movimentação de dados entre Python e C.

f2py (parte de numpy) é uma alternativa mais simples ao SWIG e boost.python para agrupar código de processamento de números C/Fortran.

Na minha experiência, existem duas maneiras fáceis de chamar o código C a partir do código Python.Existem outras abordagens, todas mais irritantes e/ou detalhadas.

A primeira e mais fácil é compilar um monte de código C como uma biblioteca compartilhada separada e depois chamar funções nessa biblioteca usando ctypes.Infelizmente, passar qualquer coisa diferente de tipos de dados básicos não é trivial.

A segunda maneira mais fácil é escrever um módulo Python em C e então chamar funções nesse módulo.Você pode passar o que quiser para essas funções C sem ter que passar por nenhum obstáculo.E é fácil chamar funções ou métodos Python a partir dessas funções C, conforme descrito aqui: https://docs.python.org/extending/extending.html#calling-python-functions-from-c

Não tenho experiência suficiente com SWIG para oferecer comentários inteligentes.E embora seja possível fazer coisas como passar objetos Python personalizados para funções C por meio de ctypes ou definir novas classes Python em C, essas coisas são irritantes e detalhadas e eu recomendo adotar uma das duas abordagens descritas acima.

Python é bastante liberal ao permitir que funções, functores e objetos sejam passados ​​​​para funções e métodos, embora eu suspeito que o mesmo não seja verdade, digamos, C ou Fortran.

Em C você não pode passar uma função como argumento para uma função, mas pode passar um ponteiro de função que é uma função igualmente boa.

Não sei o quanto isso ajudaria quando você estivesse tentando integrar código C e Python, mas só queria esclarecer um equívoco.

Além das ferramentas acima, posso recomendar o uso Pires (para criar módulos de extensão Python) ou Psicótico (como compilador JIT para Python).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top