Python - Pemrograman Ekstensi dengan C
Halaman SebelumnyaHalaman Berikutnya
Kode apa pun yang Anda tulis menggunakan bahasa yang dikompilasi seperti C, C++, atau Java dapat diintegrasikan atau diimpor ke skrip Python lain. Kode ini dianggap sebagai "ekstensi".
Modul ekstensi Python tidak lebih dari pustaka C normal. Pada mesin Unix, library ini biasanya diakhiri dengan .so (untuk objek bersama). Pada mesin Windows, Anda biasanya melihat .dll (untuk perpustakaan yang terhubung secara dinamis).
Persyaratan untuk Menulis Ekstensi
Untuk mulai menulis ekstensi, Anda memerlukan file header Python.
-
Pada mesin Unix, ini biasanya memerlukan penginstalan paket khusus pengembang seperti python2.5-dev.
-
Pengguna Windows mendapatkan header ini sebagai bagian dari paket saat mereka menggunakan penginstal Python biner.
Selain itu, diasumsikan bahwa Anda memiliki pengetahuan yang baik tentang C atau C++ untuk menulis Ekstensi Python apa pun menggunakan pemrograman C.
Pertama-tama lihat Ekstensi Python
Untuk tampilan pertama Anda pada modul ekstensi Python, Anda perlu mengelompokkan kode Anda menjadi empat bagian
File Header Python.h
Anda perlu menyertakan Python.h file header di file sumber C Anda, yang memberi Anda akses ke API Python internal yang digunakan untuk menghubungkan modul Anda ke penerjemah.
Pastikan untuk menyertakan Python.h sebelum header lain yang mungkin Anda perlukan. Anda harus mengikuti penyertaan dengan fungsi yang ingin Anda panggil dari Python.
Fungsi C
Tanda tangan implementasi C dari fungsi Anda selalu mengambil salah satu dari tiga bentuk berikut −
static PyObject *MyFunction( PyObject *self, PyObject *args );
static PyObject *MyFunctionWithKeywords(PyObject *self,
PyObject *args,
PyObject *kw);
static PyObject *MyFunctionWithNoArgs( PyObject *self );
Masing-masing dari deklarasi sebelumnya mengembalikan objek Python. Tidak ada yang namanya kekosongan function di Python seperti yang ada di C. Jika Anda tidak ingin fungsi Anda mengembalikan nilai, kembalikan C setara dengan None Python nilai. Header Python mendefinisikan makro, Py_RETURN_NONE, yang melakukan ini untuk kita.
Nama fungsi C Anda bisa apa saja yang Anda suka karena tidak pernah terlihat di luar modul ekstensi. Mereka didefinisikan sebagai statis fungsi.
Fungsi C Anda biasanya diberi nama dengan menggabungkan modul Python dan nama fungsi bersama-sama, seperti yang ditunjukkan di sini −
static PyObject *module_func(PyObject *self, PyObject *args) {
/* Do your stuff here. */
Py_RETURN_NONE;
}
Ini adalah fungsi Python yang disebut fungsi di dalam modul modul . Anda akan meletakkan pointer ke fungsi C Anda ke dalam tabel metode untuk modul yang biasanya muncul berikutnya dalam kode sumber Anda.
Tabel Pemetaan Metode
Tabel metode ini adalah array sederhana dari struktur PyMethodDef. Struktur itu terlihat seperti ini
struct PyMethodDef {
char *ml_name;
PyCFunction ml_meth;
int ml_flags;
char *ml_doc;
};
Berikut adalah deskripsi anggota struktur ini −
-
ml_name Ini adalah nama fungsi yang disajikan oleh juru bahasa Python ketika digunakan dalam program Python.
-
ml_meth Ini harus alamat ke fungsi yang memiliki salah satu tanda tangan yang dijelaskan di bagian sebelumnya.
-
ml_flags Ini memberi tahu penerjemah mana dari tiga tanda tangan yang digunakan ml_meth.
-
Bendera ini biasanya memiliki nilai METH_VAARGS.
-
Bendera ini dapat bitwise ATAU dengan METH_KEYWORDS jika Anda ingin mengizinkan argumen kata kunci ke dalam fungsi Anda.
-
Ini juga dapat memiliki nilai METH_NOARGS yang menunjukkan bahwa Anda tidak ingin menerima argumen apa pun.
-
ml_doc Ini adalah docstring untuk fungsi tersebut, yang dapat berupa NULL jika Anda tidak ingin menulisnya.
Tabel ini perlu diakhiri dengan sentinel yang terdiri dari nilai NULL dan 0 untuk anggota yang sesuai.
Contoh
Untuk fungsi yang didefinisikan di atas, kami memiliki tabel pemetaan metode berikut −
static PyMethodDef module_methods[] = {
{ "func", (PyCFunction)module_func, METH_NOARGS, NULL },
{ NULL, NULL, 0, NULL }
};
Fungsi Inisialisasi
Bagian terakhir dari modul ekstensi Anda adalah fungsi inisialisasi. Fungsi ini dipanggil oleh interpreter Python ketika modul dimuat. Fungsi tersebut harus diberi nama initModul , di mana Modul adalah nama modul.
Fungsi inisialisasi perlu diekspor dari perpustakaan yang akan Anda bangun. Header Python mendefinisikan PyMODINIT_FUNC untuk menyertakan mantra yang sesuai agar hal itu terjadi untuk lingkungan tertentu tempat kami mengompilasi. Yang harus Anda lakukan adalah menggunakannya saat mendefinisikan fungsi.
Fungsi inisialisasi C Anda umumnya memiliki struktur keseluruhan berikut
PyMODINIT_FUNC initModule() {
Py_InitModule3(func, module_methods, "docstring...");
}
Berikut adalah deskripsi Py_InitModule3 fungsi
-
menyenangkan Ini adalah fungsi yang akan diekspor.
-
modul _metode Ini adalah nama tabel pemetaan yang didefinisikan di atas.
-
doktrin Ini adalah komentar yang ingin Anda berikan di ekstensi Anda.
Menyatukan ini semua terlihat seperti berikut −
#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...");
}
Contoh
Contoh sederhana yang menggunakan semua konsep di atas
#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!");
}
Ini Py_BuildValue fungsi digunakan untuk membangun nilai Python. Simpan kode di atas dalam file hello.c. Kita akan melihat bagaimana mengkompilasi dan menginstal modul ini untuk dipanggil dari skrip Python.
Membuat dan Memasang Ekstensi
Distutil package membuatnya sangat mudah untuk mendistribusikan modul Python, baik Python murni maupun modul ekstensi, dengan cara standar. Modul didistribusikan dalam bentuk sumber dan dibuat serta diinstal melalui skrip pengaturan yang biasanya disebut setup.py sebagai berikut.
Untuk modul di atas, Anda perlu menyiapkan skrip setup.py berikut
from distutils.core import setup, Extension
setup(name='helloworld', version='1.0', \
ext_modules=[Extension('helloworld', ['hello.c'])])
Sekarang, gunakan perintah berikut, yang akan melakukan semua langkah kompilasi dan penautan yang diperlukan, dengan perintah dan flag compiler dan linker yang tepat, dan menyalin pustaka dinamis yang dihasilkan ke direktori yang sesuai −
$ python setup.py install
Pada sistem berbasis Unix, kemungkinan besar Anda harus menjalankan perintah ini sebagai root agar memiliki izin untuk menulis ke direktori paket situs. Ini biasanya bukan masalah di Windows.
Mengimpor Ekstensi
Setelah Anda menginstal ekstensi Anda, Anda akan dapat mengimpor dan memanggil ekstensi itu dalam skrip Python Anda sebagai berikut −
#!/usr/bin/python
import helloworld
print helloworld.helloworld()
Ini akan menghasilkan hasil berikut
Hello, Python extensions!!
Meneruskan Parameter Fungsi
Karena kemungkinan besar Anda ingin mendefinisikan fungsi yang menerima argumen, Anda dapat menggunakan salah satu tanda tangan lain untuk fungsi C Anda. Misalnya, fungsi berikut, yang menerima sejumlah parameter, akan didefinisikan seperti ini −
static PyObject *module_func(PyObject *self, PyObject *args) {
/* Parse args and do something interesting here. */
Py_RETURN_NONE;
}
Tabel metode yang berisi entri untuk fungsi baru akan terlihat seperti ini
static PyMethodDef module_methods[] = {
{ "func", (PyCFunction)module_func, METH_NOARGS, NULL },
{ "func", module_func, METH_VARARGS, NULL },
{ NULL, NULL, 0, NULL }
};
Anda dapat menggunakan API PyArg_ParseTuple fungsi untuk mengekstrak argumen dari satu pointer PyObject yang diteruskan ke fungsi C Anda.
Argumen pertama untuk PyArg_ParseTuple adalah argumen args. Ini adalah objek yang akan Anda parsing . Argumen kedua adalah string format yang menjelaskan argumen seperti yang Anda harapkan muncul. Setiap argumen diwakili oleh satu atau lebih karakter dalam format string sebagai berikut.
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;
}
Mengkompilasi versi baru modul Anda dan mengimpornya memungkinkan Anda untuk memanggil fungsi baru dengan sejumlah argumen jenis apa pun −
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)
Anda mungkin dapat membuat lebih banyak variasi.
PyArg_ParseTuple Fungsi
Berikut adalah tanda tangan standar untuk PyArg_ParseTuple fungsi
int PyArg_ParseTuple(PyObject* tuple,char* format,...)
Fungsi ini mengembalikan 0 untuk kesalahan, dan nilai yang tidak sama dengan 0 untuk keberhasilan. tuple adalah PyObject* yang merupakan argumen kedua fungsi C. Berikut format adalah string C yang menjelaskan argumen wajib dan opsional.
Berikut adalah daftar kode format untuk PyArg_ParseTuple fungsi
Kode | Tipe C | Arti |
c | char | String Python dengan panjang 1 menjadi karakter C. |
d | ganda | Float Python menjadi double C. |
f | mengambang | Apungan Python menjadi pelampung C. |
i | int | Int Python menjadi int C. |
l | panjang | Int Python menjadi panjang C. |
L | panjang panjang | Int Python menjadi panjang C |
O | PyObject* | Mendapatkan referensi pinjaman non-NULL untuk argumen Python. |
s | char* | String python tanpa null yang disematkan ke karakter C*. |
s# | char*+int | Semua string Python ke alamat dan panjang C. |
t# | char*+int | Buffer segmen tunggal read-only ke alamat dan panjang C. |
u | Py_UNICODE* | Python Unicode tanpa menyematkan null ke C. |
u# | Py_UNICODE*+int | Alamat dan panjang Python Unicode C apa saja. |
w# | char*+int | Baca/tulis buffer segmen tunggal ke alamat dan panjang C. |
z | char* | Seperti s, juga menerima None (menyetel C char* ke NULL). |
z# | char*+int | Seperti s#, juga menerima None (set C char* ke NULL). |
(...) | sesuai ... | Urutan Python diperlakukan sebagai satu argumen per item. |
| | Argumen berikut ini opsional. |
| | Format akhir, diikuti dengan nama fungsi untuk pesan kesalahan. |
| | Format berakhir, diikuti oleh seluruh teks pesan kesalahan. |
Mengembalikan Nilai
Py_BuildValue mengambil format string seperti PyArg_ParseTuple melakukan. Alih-alih meneruskan alamat nilai yang Anda buat, Anda meneruskan nilai sebenarnya. Berikut adalah contoh yang menunjukkan cara menerapkan fungsi tambah −
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);
}
Beginilah jadinya jika diimplementasikan dengan Python
def add(a, b):
return (a + b)
Anda dapat mengembalikan dua nilai dari fungsi Anda sebagai berikut, ini akan ditangkap menggunakan daftar dengan 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);
}
Beginilah jadinya jika diimplementasikan dengan Python
def add_subtract(a, b):
return (a + b, a - b)
Py_BuildValue Fungsi
Berikut adalah tanda tangan standar untuk Py_BuildValue fungsi
PyObject* Py_BuildValue(char* format,...)
Berikut format adalah string C yang menjelaskan objek Python untuk dibangun. Argumen berikut dari Py_BuildValue adalah nilai C dari mana hasilnya dibangun. PyObject* hasilnya adalah referensi baru.
Tabel berikut mencantumkan string kode yang umum digunakan, di mana nol atau lebih digabungkan ke dalam format string.
Kode | Tipe C | Arti |
c | char | A C char menjadi string Python dengan panjang 1. |
d | ganda | A C double menjadi float Python. |
f | mengambang | Apungan C menjadi pelampung Python. |
i | int | A C int menjadi Python int. |
l | panjang | Panjang C menjadi int Python. |
N | PyObject* | Meneruskan objek Python dan mencuri referensi. |
O | PyObject* | Meneruskan objek Python dan memasukkannya seperti biasa. |
O& | convert+void* | Konversi sewenang-wenang |
s | char* | C 0-dihentikan char* ke string Python, atau NULL ke None. |
s# | char*+int | C char* dan panjangnya ke string Python, atau NULL ke None. |
u | Py_UNICODE* | C-wide, string diakhiri null ke Python Unicode, atau NULL ke None. |
u# | Py_UNICODE*+int | String lebar-C dan panjang ke Python Unicode, atau NULL ke None. |
w# | char*+int | Baca/tulis buffer segmen tunggal ke alamat dan panjang C. |
z | char* | Seperti s, juga menerima None (menyetel C char* ke NULL). |
z# | char*+int | Seperti s#, juga menerima None (menyetel C char* ke NULL). |
(...) | sesuai ... | Membangun tupel Python dari nilai C. |
[...] | sesuai ... | Membuat daftar Python dari nilai C. |
{...} | sesuai ... | Membangun kamus Python dari nilai C, kunci dan nilai bergantian. |
Kode {...} membangun kamus dari jumlah nilai C yang genap, secara bergantian kunci dan nilai. Misalnya, Py_BuildValue("{issi}",23,"zig","zag",42) mengembalikan kamus seperti {23:'zig','zag':42} Python.