Combinazione di funzioni C e Python in un modulo
Domanda
Ho un modulo di estensione C, al quale vorrei aggiungere alcune funzioni di utilità Python. Esiste un modo consigliato per farlo?
Ad esempio:
import my_module
my_module.super_fast_written_in_C()
my_module.written_in_Python__easy_to_maintain()
Sono principalmente interessato a Python 2.x.
Soluzione
Il solito modo per farlo è: mymod.py contiene le funzioni di utilità scritte in Python e importa le chicche nel modulo _mymod che è scritto in C e viene importato da _mymod.so o _mymod.pyd. Ad esempio, guarda ... / Lib / csv.py nella tua distribuzione Python.
Altri suggerimenti
Prefisso l'estensione nativa con un trattino basso. Quindi, in Python, crea un modulo wrapper che importa quell'estensione nativa e aggiunge anche altre routine non native.
Le risposte esistenti descrivono il metodo più spesso usato: ha il potenziale vantaggio di consentire implementazioni pure-Python (o altro linguaggio) su piattaforme in cui l'estensione C compilata non è disponibile (inclusi Jython e IronPython).
In alcuni casi, tuttavia, potrebbe non essere utile suddividere il modulo in un livello C e un livello Python solo per fornire alcuni extra scritti in modo più sensato in Python rispetto a C. Ad esempio, gmpy (righe 7113 e seguenti), in ordine per abilitare il decapaggio delle istanze del tipo gmpy
, usa:
copy_reg_module = PyImport_ImportModule("copy_reg");
if (copy_reg_module) {
char* enable_pickle =
"def mpz_reducer(an_mpz): return (gmpy.mpz, (an_mpz.binary(), 256))\n"
"def mpq_reducer(an_mpq): return (gmpy.mpq, (an_mpq.binary(), 256))\n"
"def mpf_reducer(an_mpf): return (gmpy.mpf, (an_mpf.binary(), 0, 256))\n"
"copy_reg.pickle(type(gmpy.mpz(0)), mpz_reducer)\n"
"copy_reg.pickle(type(gmpy.mpq(0)), mpq_reducer)\n"
"copy_reg.pickle(type(gmpy.mpf(0)), mpf_reducer)\n"
;
PyObject* namespace = PyDict_New();
PyObject* result = NULL;
if (options.debug)
fprintf(stderr, "gmpy_module imported copy_reg OK\n");
PyDict_SetItemString(namespace, "copy_reg", copy_reg_module);
PyDict_SetItemString(namespace, "gmpy", gmpy_module);
PyDict_SetItemString(namespace, "type", (PyObject*)&PyType_Type);
result = PyRun_String(enable_pickle, Py_file_input,
namespace, namespace);
Se desideri che quelle poche funzioni extra rimangano " nel tuo modulo (non necessario in questo caso di esempio), ovviamente utilizzeresti l'oggetto module come costruito da Py_InitModule3
(o qualunque altro metodo) e il suo PyModule_GetDict
anziché un dizionario temporaneo come spazio dei nomi in cui PyRun_String
. E naturalmente ci sono approcci più sofisticati rispetto a PyRun_String
le istruzioni def
e class
di cui hai bisogno, ma, per casi abbastanza semplici, questo semplice approccio potrebbe in effetti essere sufficiente.