Domanda

È da un po' che rifletto sulla possibilità di scrivere una libreria adatta al meglio.Conosco Python abbastanza bene e ho intenzione di implementare tutto in Python per cominciare, ma prevedo che alla fine potrei dover reimplementare alcune routine fondamentali in un linguaggio compilato.

IIRC, uno dei compiti originali di Python era quello di linguaggio di prototipazione, tuttavia Python è piuttosto liberale nel consentire il passaggio di funzioni, funtori e oggetti a funzioni e metodi, mentre sospetto che lo stesso non sia vero per esempio C o Fortran.

Cosa dovrei sapere sulla progettazione di funzioni/classi che prevedo dovranno interfacciarsi con il linguaggio compilato?E quanti di questi potenziali problemi vengono risolti da librerie come cTypes, bgen, SWIG, Boost.Python, Citone O Python SIP?

Per questo particolare caso d'uso (una libreria di adattamento) immagino di consentire agli utenti di definire funzioni matematiche (Guassiano, Lorentziano ecc.) come funzioni Python che possono quindi essere passate e interpretate dalla libreria di adattamento del codice compilato.Anche il passaggio e la restituzione degli array è essenziale.

È stato utile?

Soluzione

Finalmente una domanda a cui posso davvero dare una risposta di valore :).

Ho studiato f2py, boost.python, swig, cython e pyrex per il mio lavoro (dottorato in tecniche di misurazione ottica).Ho usato ampiamente swig, boost.python alcuni e pyrex e cython molto.Ho usato anche ctypes.Questa è la mia ripartizione:

Disclaimer:Questa è la mia esperienza personale.Non sono coinvolto in nessuno di questi progetti.

sorso:non funziona bene con C++.Dovrebbe, ma i problemi di modifica dei nomi nella fase di collegamento sono stati un grosso grattacapo per me su Linux e Mac OS X.Se hai codice C e vuoi che sia interfacciato con Python, è una buona soluzione.Ho confezionato GTS per le mie esigenze e avevo bisogno di scrivere fondamentalmente una libreria condivisa C a cui potermi connettere.Non lo raccomanderei.

Tipi di C:Ho scritto un wrapper libdc1394 (libreria fotocamera IEEE) utilizzando ctypes ed è stata un'esperienza molto semplice.Puoi trovare il codice su https://launchpad.net/pydc1394.Convertire le intestazioni in codice Python richiede molto lavoro, ma poi tutto funziona in modo affidabile.Questo è un buon modo se vuoi interfacciare una libreria esterna.Ctypes è anche nello stdlib di Python, quindi tutti possono utilizzare subito il tuo codice.Questo è anche un buon modo per giocare rapidamente con una nuova libreria in Python.Posso consigliarlo per interfacciarsi con librerie esterne.

Boost.Python:Molto piacevole.Se hai già del codice C++ che desideri utilizzare in Python, scegli questo.È molto semplice tradurre le strutture di classi C++ in strutture di classi Python in questo modo.Lo consiglio se hai il codice C++ di cui hai bisogno in Python.

Pirex/Cython: Usa Cython, non Pyrex.Periodo.Cython è più avanzato e più divertente da usare.Al giorno d'oggi, con Cython faccio tutto quello che facevo con SWIG o Ctypes.È anche il modo migliore se hai codice Python che funziona troppo lentamente.Il processo è assolutamente fantastico:converti i tuoi moduli Python in moduli Cython, li costruisci e continui a profilare e ottimizzare come se fosse ancora Python (non è necessario alcun cambio di strumenti).È quindi possibile applicare tanto (o poco) codice C mescolato con il codice Python.Questo è di gran lunga più veloce rispetto al dover riscrivere intere parti della tua applicazione in C;riscrivi solo il ciclo interno.

Tempi:ctypes ha il sovraccarico di chiamata più elevato (~700ns), seguito da boost.python (322ns), quindi direttamente da swig (290ns).Cython ha l'overhead di chiamata più basso (124ns) e il miglior feedback su cui trascorre il tempo (supporto cProfile!).I numeri provengono dalla mia casella che chiama una funzione banale che restituisce un numero intero da una shell interattiva;Il sovraccarico di importazione del modulo pertanto non è cronometrato, lo è solo il sovraccarico della chiamata di funzione.È quindi più semplice e produttivo ottenere velocemente il codice Python profilando e utilizzando Cython.

Riepilogo:Per il tuo problema, usa Cython ;).Spero che questa carrellata possa essere utile a qualcuno.Risponderò volentieri a qualsiasi domanda rimanente.


Modificare:Dimentico di menzionare:per scopi numerici (ovvero la connessione a NumPy) utilizzare Cython;ne hanno il supporto (perché fondamentalmente sviluppano cython per questo scopo).Quindi questo dovrebbe essere un altro +1 per la tua decisione.

Altri suggerimenti

Non ho usato SWIG o SIP, ma trovo che scriva wrapper Python con boost.python essere molto potente e relativamente facile da usare.

Non sono chiaro quali siano i tuoi requisiti per il passaggio di tipi tra C/C++ e Python, ma puoi farlo facilmente esponendo un tipo C++ a Python o utilizzando un generico boost::python::oggetto argomento alla tua API C++.Puoi anche registrare convertitori per convertire automaticamente i tipi Python in tipi C++ e viceversa.

Se prevedi di utilizzare boost.python, il file tutorial è un buon punto di partenza.

Ho implementato qualcosa in qualche modo simile a quello di cui hai bisogno.Ho una funzione C ++ che accetta una funzione Python e un'immagine come argomenti e applica la funzione Python a ciascun pixel nell'immagine.

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;
}

In questo caso, Image è un oggetto C++ esposto a Python (un'immagine con pixel float) e op è una funzione definita da Python (o in realtà qualsiasi oggetto Python con un attributo __call__).È quindi possibile utilizzare questa funzione come segue (assumendo che unario si trovi nell'immagine chiamata che contiene anche Immagine e una funzione di caricamento):

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

Per quanto riguarda l'utilizzo degli array con boost, personalmente non l'ho fatto, ma so che è disponibile la funzionalità per esporre gli array a Python utilizzando boost - Questo potrebbe essere utile.

Il modo migliore per pianificare un'eventuale transizione al codice compilato è scrivere le parti sensibili alle prestazioni come un modulo di funzioni semplici in un stile funzionale (senza stato e senza effetti collaterali), che accettano e restituiscono tipi di dati di base.

Ciò fornirà una mappatura uno a uno dal codice del prototipo Python all'eventuale codice compilato e ti consentirà di utilizzare ctypes facilmente ed evitare un sacco di mal di testa.

Per l'adattamento di picco, quasi sicuramente avrai bisogno di usare gli array, il che complicherà un po' le cose, ma è comunque molto fattibile con i ctype.

Se vuoi davvero utilizzare strutture dati più complicate o modificare gli argomenti passati, SWIG O L'interfaccia di estensione C standard di Python ti lascerà fare quello che vuoi, ma con una certa seccatura.

Per quello che stai facendo, potresti anche voler dare un'occhiata NumPy, che potrebbe svolgere parte del lavoro che vorresti inviare a C, oltre a offrire qualche ulteriore aiuto nello spostamento dei dati avanti e indietro tra Python e C.

f2py (parte di numpy) è un'alternativa più semplice a SWIG e boost.python per avvolgere il codice di elaborazione dei numeri C/Fortran.

Nella mia esperienza, ci sono due semplici modi per richiamare il codice C dal codice Python.Esistono altri approcci, tutti più fastidiosi e/o prolissi.

Il primo e più semplice è compilare un gruppo di codice C come libreria condivisa separata e quindi chiamare le funzioni in quella libreria utilizzando ctypes.Sfortunatamente, passare qualsiasi cosa diversa dai tipi di dati di base non è banale.

Il secondo modo più semplice è scrivere un modulo Python in C e quindi chiamare le funzioni in quel modulo.Puoi passare tutto ciò che vuoi a queste funzioni C senza dover fare i salti mortali.Ed è facile chiamare funzioni o metodi Python da queste funzioni C, come descritto qui: https://docs.python.org/extending/extending.html#calling-python-functions-from-c

Non ho abbastanza esperienza con SWIG per offrire commenti intelligenti.E sebbene sia possibile fare cose come passare oggetti Python personalizzati a funzioni C tramite ctype o definire nuove classi Python in C, queste cose sono fastidiose e prolisse e consiglio di adottare uno dei due approcci descritti sopra.

Python è piuttosto liberale nel consentire il passaggio di funzioni, funtori e oggetti a funzioni e metodi, mentre sospetto che lo stesso non sia vero, ad esempio, per C o Fortran.

In C non è possibile passare una funzione come argomento a una funzione ma è possibile passare un puntatore a funzione che è una funzione altrettanto valida.

Non so quanto ciò potrebbe aiutare quando si tenta di integrare il codice C e Python, ma volevo solo chiarire un malinteso.

Oltre agli strumenti di cui sopra, posso consigliare l'utilizzo Pirex (per creare moduli di estensione Python) o Psico (come compilatore JIT per Python).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top