PYTHON

1. Memperluas Python dengan C atau C ++ 

Sangat mudah untuk menambahkan modul built-in baru ke Python, jika Anda tahu cara memprogram dalam C. Modul ekstensi seperti itu dapat melakukan dua hal yang tidak dapat dilakukan secara langsung dengan Python: mereka dapat mengimplementasikan tipe objek built-in yang baru, dan mereka dapat memanggil fungsi pustaka C dan panggilan sistem.
Untuk mendukung ekstensi, Python API (Application Programmers Interface) mendefinisikan satu set fungsi, makro dan variabel yang menyediakan akses ke sebagian besar aspek dari sistem run-time Python. Python API dimasukkan dalam file sumber C dengan menyertakan header "Python.h".
Kompilasi modul ekstensi tergantung pada penggunaan yang dimaksudkan serta pada pengaturan sistem Anda; perincian diberikan dalam bab-bab selanjutnya.
Catatan

Antarmuka C ekstensi khusus untuk CPython, dan modul ekstensi tidak bekerja pada implementasi Python lainnya. Dalam banyak kasus, adalah mungkin untuk menghindari penulisan C ekstensi dan melestarikan portabilitas ke implementasi lain. Misalnya, jika kasus penggunaan Anda memanggil fungsi pustaka C atau panggilan sistem, Anda harus mempertimbangkan menggunakan ctypesmodul atau pustaka cffi daripada menulis kode C khusus. Modul-modul ini memungkinkan Anda menulis kode Python untuk berinteraksi dengan kode C dan lebih portabel antara implementasi Python daripada menulis dan menyusun modul ekstensi C.

1.1. Contoh Sederhana 

Mari kita membuat modul ekstensi yang disebut spam(makanan favorit penggemar Monty Python ...) dan katakanlah kita ingin membuat antarmuka Python ke fungsi C library system() [1] . Fungsi ini mengambil string karakter null-dihentikan sebagai argumen dan mengembalikan integer. Kami ingin fungsi ini dapat dipanggil dari Python sebagai berikut:
>>> import spam
>>> status = spam.system("ls -l")
Mulailah dengan membuat file spammodule.c(Secara historis, jika sebuah modul dipanggil spam, file C yang berisi penerapannya disebut spammodule.c; jika nama modul sangat panjang, seperti spammify, nama modul bisa saja spammify.c.)
Baris pertama file kami dapat berupa:
#include <Python.h>
yang menarik API Python (Anda dapat menambahkan komentar yang menjelaskan tujuan modul dan pemberitahuan hak cipta jika Anda suka).
Catatan

Karena Python dapat mendefinisikan beberapa definisi pra-prosesor yang mempengaruhi header standar pada beberapa sistem, Andaharus menyertakan Python.hsebelum header standar disertakan.
Semua simbol yang terlihat pengguna didefinisikan dengan Python.hmemiliki awalan Pyatau PY, kecuali yang ditentukan dalam file header standar. Untuk kenyamanan, dan karena mereka digunakan secara luas oleh interpreter Python, "Python.h" termasuk file header standar beberapa: <stdio.h><string.h><errno.h>, dan <stdlib.h>Jika file header yang terakhir tidak ada di sistem Anda, ia menyatakan fungsi malloc()free()dan realloc()secara langsung.
Hal berikutnya yang kami tambahkan ke file modul kami adalah fungsi C yang akan dipanggil ketika ekspresi Python spam.system(string)dievaluasi (kita akan segera melihat bagaimana akhirnya dipanggil):
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return Py_BuildValue("i", sts);
}
Ada terjemahan langsung dari daftar argumen dengan Python (misalnya, ekspresi tunggal ) ke argumen yang dilewatkan ke fungsi C. Fungsi C selalu memiliki dua argumen, bernama diri dan argsi ."ls -l"
Untuk fungsi modul, argumen diri adalah NULL atau pointer yang dipilih saat menginisialisasi modul (lihat Py_InitModule4()). Untuk metode, itu akan mengarah ke objek contoh.
The args argumen akan menjadi pointer ke objek Python tuple yang berisi argumen. Setiap item dari tuple sesuai dengan argumen dalam daftar argumen panggilan. Argumennya adalah objek Python - untuk melakukan apa saja dengan mereka dalam fungsi C kita harus mengkonversinya menjadi nilai C. Fungsi PyArg_ParseTuple()di Python API memeriksa jenis argumen dan mengonversinya menjadi nilai C. Ini menggunakan string template untuk menentukan jenis yang diperlukan dari argumen serta jenis variabel C yang digunakan untuk menyimpan nilai yang dikonversi. Lebih lanjut tentang ini nanti.
PyArg_ParseTuple()mengembalikan true (non-nol) jika semua argumen memiliki tipe yang tepat dan komponennya telah disimpan dalam variabel yang alamatnya dilewatkan. Ini mengembalikan false (nol) jika daftar argumen yang tidak valid dilewatkan. Dalam kasus terakhir ini juga memunculkan eksepsi yang sesuai sehingga fungsi pemanggilan dapat mengembalikan NULL dengan segera (seperti yang kita lihat pada contoh).

1.2. Intermezzo: Kesalahan dan Pengecualian 

Sebuah konvensi penting di seluruh interpreter Python adalah sebagai berikut: ketika suatu fungsi gagal, ia harus menetapkan kondisi pengecualian dan mengembalikan nilai kesalahan (biasanya penunjuk NULL ). Pengecualian disimpan dalam variabel global statis di dalam interpreter; jika variabel ini tidak ada pengecualian NULL telah terjadi. Variabel global kedua menyimpan "nilai terkait" dari pengecualian (argumen kedua raise). Variabel ketiga berisi stack traceback dalam kasus kesalahan berasal kode Python. Ketiga variabel ini adalah persamaan C dari variabel Python sys.exc_type,sys.exc_valuedan sys.exc_traceback(lihat bagian pada modul sysdalam Referensi Perpustakaan Python). Penting untuk mengetahui tentang mereka untuk memahami bagaimana kesalahan dilewatkan.
API Python mendefinisikan sejumlah fungsi untuk mengatur berbagai jenis pengecualian.
Yang paling umum adalah PyErr_SetString()Argumennya adalah objek pengecualian dan string C. Objek pengecualian biasanya objek yang telah ditentukan seperti PyExc_ZeroDivisionErrorString C menunjukkan penyebab kesalahan dan diubah menjadi objek string Python dan disimpan sebagai "nilai terkait" dari pengecualian.
Fungsi lain yang berguna adalah PyErr_SetFromErrno(), yang hanya mengambil argumen pengecualian dan membangun nilai yang terkait dengan pemeriksaan variabel global errnoFungsi yang paling umum adalah PyErr_SetObject(), yang mengambil dua argumen objek, pengecualian dan nilai yang terkait. Anda tidak perlu Py_INCREF()objek yang dilewatkan ke salah satu fungsi ini.
Anda dapat menguji secara non-destruktif apakah pengecualian telah ditetapkan PyErr_Occurred()Ini mengembalikan objek eksepsi saat ini, atau NULLjika tidak ada pengecualian. Anda biasanya tidak perlu menelepon PyErr_Occurred()untuk melihat apakah kesalahan terjadi pada panggilan fungsi, karena Anda harus dapat memberi tahu dari nilai kembalian.
Ketika fungsi f yang memanggil fungsi lain g mendeteksi bahwa yang terakhir gagal, f harus mengembalikan nilai kesalahan (biasanya NULL atau -1). Seharusnya tidak memanggil salah satu PyErr_*()fungsi - salah satu telah dipanggil oleh g . f pemanggil kemudian seharusnya juga kembali indikasi kesalahan untuk yang pemanggil, sekali lagi tanpa menyebutPyErr_*(), dan seterusnya - penyebab paling mendetail dari kesalahan telah dilaporkan oleh fungsi yang pertama kali terdeteksi. Setelah kesalahan mencapai loop utama Python interpreter, ini membatalkan kode Python saat ini mengeksekusi dan mencoba untuk menemukan pengendali pengecualian ditentukan oleh programmer Python.
(Ada situasi di mana sebuah modul benar-benar dapat memberikan pesan kesalahan yang lebih rinci dengan memanggil PyErr_*()fungsi lain , dan dalam kasus seperti itu baik-baik saja untuk melakukannya. Sebagai aturan umum, bagaimanapun, ini tidak diperlukan, dan dapat menyebabkan informasi tentang penyebabnya kesalahan yang hilang: sebagian besar operasi dapat gagal karena berbagai alasan.)
Untuk mengabaikan pengecualian yang ditetapkan oleh pemanggilan fungsi yang gagal, kondisi pengecualian harus dihapus secara eksplisit dengan memanggil PyErr_Clear()Satu-satunya waktu kode C harus memanggil PyErr_Clear()adalah jika tidak ingin menyampaikan kesalahan ke interpreter tetapi ingin menangani sepenuhnya dengan sendirinya (mungkin dengan mencoba sesuatu yang lain, atau berpura-pura tidak ada yang salah).
Setiap malloc()panggilan yang gagal harus diubah menjadi pengecualian - pemanggil langsung dari malloc()(atau realloc()) harus memanggilPyErr_NoMemory()dan mengembalikan indikator kegagalan itu sendiri. Semua fungsi pembuatan objek (misalnya, PyInt_FromLong()) sudah melakukan ini, jadi catatan ini hanya relevan bagi mereka yang menelepon malloc()secara langsung.
Juga perhatikan bahwa, dengan pengecualian PyArg_ParseTuple()dan teman yang penting, fungsi yang mengembalikan status integer biasanya mengembalikan nilai positif atau nol untuk sukses dan -1untuk kegagalan, seperti panggilan sistem Unix.
Akhirnya, berhati-hatilah untuk membersihkan sampah (dengan membuat Py_XDECREF()atau Py_DECREF()memanggil objek yang telah Anda buat) ketika Anda mengembalikan indikator kesalahan!
Pilihan pengecualian untuk dibesarkan sepenuhnya milik Anda. Ada objek C yang telah ditetapkan yang sesuai dengan semua pengecualian Python bawaan, seperti PyExc_ZeroDivisionError, yang dapat Anda gunakan secara langsung. Tentu saja, Anda harus memilih pengecualian dengan bijak - jangan gunakan PyExc_TypeErroruntuk mengartikan bahwa file tidak dapat dibuka (yang mungkin seharusnya PyExc_IOError). Jika ada yang salah dengan daftar argumen, PyArg_ParseTuple() fungsi biasanya naik PyExc_TypeErrorJika Anda memiliki argumen yang nilainya harus dalam kisaran tertentu atau harus memenuhi persyaratan lain, PyExc_ValueErroradalah tepat.
Anda juga dapat menetapkan pengecualian baru yang unik untuk modul Anda. Untuk ini, Anda biasanya mendeklarasikan variabel objek statis di awal file Anda:
static PyObject *SpamError;
dan inisialisasi dalam fungsi inisialisasi modul Anda ( initspam()) dengan objek pengecualian (kecuali memeriksa kesalahan saat ini):
PyMODINIT_FUNC
initspam(void)
{
    PyObject *m;

    m = Py_InitModule("spam", SpamMethods);
    if (m == NULL)
        return;

    SpamError = PyErr_NewException("spam.error", NULL, NULL);
    Py_INCREF(SpamError);
    PyModule_AddObject(m, "error", SpamError);
}
Perhatikan bahwa nama Python untuk objek pengecualian spam.errorThe PyErr_NewException()Fungsi dapat membuat kelas dengan makhluk kelas dasar Exception(kecuali kelas lain dilewatkan di bukannya NULL ), dijelaskan dalam Pengecualian Built-in .
Perhatikan juga bahwa SpamErrorvariabel mempertahankan referensi ke kelas pengecualian yang baru dibuat; ini disengaja! Karena pengecualian dapat dihapus dari modul dengan kode eksternal, referensi yang dimiliki ke kelas diperlukan untuk memastikan bahwa itu tidak akan dibuang, menyebabkan SpamErrormenjadi pointer menjuntai. Jika itu menjadi pointer yang menggantung, kode C yang memunculkan eksepsi dapat menyebabkan dump inti atau efek samping yang tidak diinginkan.
Kami membahas penggunaan PyMODINIT_FUNCsebagai tipe pengembalian fungsi di kemudian hari dalam contoh ini.
The spam.errorpengecualian dapat diangkat dalam modul ekstensi Anda menggunakan panggilan untuk PyErr_SetString()seperti yang ditunjukkan di bawah ini:
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    if (sts < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}

1.3. Kembali ke Contoh 

Kembali ke contoh fungsi kami, Anda sekarang harus dapat memahami pernyataan ini:
if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;
Ini mengembalikan NULL (indikator kesalahan untuk fungsi mengembalikan pointer objek) jika kesalahan terdeteksi dalam daftar argumen, bergantung pada pengecualian yang ditetapkan oleh PyArg_ParseTuple()Jika tidak, nilai string argumen telah disalin ke variabel lokal commandIni adalah penugasan penunjuk dan Anda tidak seharusnya memodifikasi string yang ditunjuknya (jadi dalam Standar C, variabel commandharus dinyatakan dengan benar ).const char *command
Pernyataan selanjutnya adalah panggilan ke fungsi Unix system(), meneruskan string yang baru kami dapatkan dari PyArg_ParseTuple():
sts = system(command);
spam.system()Fungsi kami harus mengembalikan nilai stssebagai objek Python. Ini dilakukan dengan menggunakan fungsi Py_BuildValue(), yang merupakan sesuatu seperti kebalikan dari PyArg_ParseTuple(): ia mengambil string format dan sejumlah nilai C, dan mengembalikan objek Python baru. Info lebih lanjut Py_BuildValue()diberikan nanti.
return Py_BuildValue("i", sts);
Dalam hal ini, ia akan mengembalikan objek integer. (Ya, bahkan bilangan bulat adalah objek di heap dengan Python!)
Jika Anda memiliki fungsi C yang mengembalikan tidak ada argumen yang berguna (fungsi yang kembali void), fungsi Python yang sesuai harus kembali NoneAnda perlu idiom ini untuk melakukannya (yang diimplementasikan oleh Py_RETURN_NONE makro):
Py_INCREF(Py_None);
return Py_None;
Py_Noneadalah nama C untuk objek Python khusus NoneIni adalah objek Python asli daripada penunjuk NULL , yang berarti "kesalahan" dalam sebagian besar konteks, seperti yang telah kita lihat.

1.4. Tabel Metode Modul dan Fungsi Inisialisasi 

Saya berjanji untuk menunjukkan bagaimana spam_system()dipanggil dari program Python. Pertama, kita perlu daftar nama dan alamatnya dalam "tabel metode":
static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};
Perhatikan entri ketiga ( METH_VARARGS). Ini adalah bendera yang memberitahukan interpreter konvensi pemanggilan yang akan digunakan untuk fungsi C. Biasanya harus selalu METH_VARARGSatau nilai berarti bahwa varian usang digunakan.METH_VARARGS | METH_KEYWORDS0PyArg_ParseTuple()
Ketika hanya menggunakan METH_VARARGS, fungsi harus mengharapkan parameter tingkat Python dilewatkan sebagai tupel yang dapat diterima untuk parsing via PyArg_ParseTuple()informasi lebih lanjut tentang fungsi ini disediakan di bawah ini.
The METH_KEYWORDSbit mungkin diatur di bidang ketiga jika argumen kata kunci harus dilewatkan ke fungsi. Dalam hal ini, fungsi C harus menerima parameter ketiga yang akan menjadi kamus kata kunci. Gunakan untuk mengurai argumen ke fungsi seperti itu.PyObject *PyArg_ParseTupleAndKeywords()
Tabel metode harus diteruskan ke interpreter dalam fungsi inisialisasi modul. Fungsi inisialisasi harus diberi nama initname(), di mana nama adalah nama modul, dan harus menjadi satu-satunya non- staticitem yang didefinisikan dalam file modul:
PyMODINIT_FUNC
initspam(void)
{
    (void) Py_InitModule("spam", SpamMethods);
}
Perhatikan bahwa PyMODINIT_FUNC menyatakan fungsi sebagai voidtipe kembalian, menyatakan deklarasi tautan khusus yang diperlukan oleh platform, dan untuk C + + menyatakan fungsi sebagai .extern "C"
Ketika modul impor Python spamuntuk pertama kalinya, initspam()dipanggil. (Lihat di bawah ini untuk komentar tentang menanamkan Python.) Ini panggilan Py_InitModule(), yang menciptakan "objek modul" (yang dimasukkan dalam kamus di sys.modulesbawah kunci "spam"), dan menyisipkan fungsi objek built-in ke dalam modul yang baru dibuat berdasarkan pada tabel (sebuah susunan PyMethodDefstruktur) yang dilewatkan sebagai argumen kedua. Py_InitModule()mengembalikan pointer ke objek modul yang dibuatnya (yang tidak digunakan di sini). Mungkin batalkan dengan kesalahan fatal untuk kesalahan tertentu, atau kembalikan NULL jika modul tidak dapat diinisialisasi dengan memuaskan.
Ketika menanamkan Python, initspam()fungsi ini tidak dipanggil secara otomatis kecuali ada entri di dalam _PyImport_Inittabtabel. Cara termudah untuk menangani ini adalah secara statis menginisialisasi modul Anda yang terhubung secara statis dengan langsung menelepon initspam()setelah panggilan ke Py_Initialize():
int
main(int argc, char *argv[])
{
    /* Pass argv[0] to the Python interpreter */
    Py_SetProgramName(argv[0]);

    /* Initialize the Python interpreter.  Required. */
    Py_Initialize();

    /* Add a static module */
    initspam();

    ...
Contoh dapat ditemukan dalam file Demo/embed/demo.cdalam distribusi sumber Python.
Catatan

Menghapus entri dari sys.modulesatau mengimpor modul yang dikompilasi menjadi beberapa interpreter dalam suatu proses (atau mengikuti fork()tanpa campur tangan exec()) dapat menimbulkan masalah untuk beberapa modul ekstensi. Penulis modul ekstensi harus berhati-hati ketika menginisialisasi struktur data internal. Perhatikan juga bahwa reload()fungsi dapat digunakan dengan modul ekstensi, dan akan memanggil fungsi inisialisasi modul ( initspam()dalam contoh), tetapi tidak akan memuat modul lagi jika dimuat dari file objek yang dapat dimuat secara dinamis (.sodi Unix, .dllpada Windows).
Modul contoh yang lebih substansial termasuk dalam distribusi sumber Python sebagai Modules/xxmodule.cFile ini dapat digunakan sebagai template atau hanya dibaca sebagai contoh.

1.5. Kompilasi dan Keterkaitan 

Ada dua hal yang harus dilakukan sebelum Anda dapat menggunakan ekstensi baru Anda: mengkompilasi dan menautkannya dengan sistem Python. Jika Anda menggunakan pemuatan dinamis, detailnya mungkin bergantung pada gaya pemuatan dinamis yang digunakan sistem Anda; lihat bab tentang membangun modul ekstensi (bab Building C dan C ++ Extensions dengan distutils ) dan informasi tambahan yang hanya berkaitan dengan pembangunan di Windows (bab Building C dan C ++ Extensions di Windows ) untuk informasi lebih lanjut tentang ini.
Jika Anda tidak dapat menggunakan pemuatan dinamis, atau jika Anda ingin menjadikan modul Anda sebagai bagian permanen dari interpreter Python, Anda harus mengubah konfigurasi konfigurasi dan membangun kembali interpreter. Untungnya, ini sangat sederhana di Unix: cukup tempatkan file Anda ( spammodule.cmisalnya) di Modules/direktori distribusi sumber yang belum dipaketkan, tambahkan baris ke file yang Modules/Setup.localmenjelaskan file Anda:
spam spammodule.o
dan membangun kembali interpreter dengan menjalankan make di direktori toplevel. Anda juga dapat menjalankan make dalam Modules/ subdirektori, tetapi kemudian Anda harus terlebih dahulu membangunnya kembali Makefiledengan menjalankan ' make Makefile'. (Ini diperlukan setiap kali Anda mengubah Setupfile.)
Jika modul Anda memerlukan pustaka tambahan untuk ditautkan, ini dapat dicantumkan pada baris dalam file konfigurasi juga, misalnya:
spam spammodule.o -lX11

1,6. Memanggil Fungsi Python dari C 

Sejauh ini kami berkonsentrasi untuk membuat fungsi C dapat dipanggil dari Python. Pembalikan juga berguna: memanggil fungsi Python dari C. Hal ini terutama berlaku untuk pustaka yang mendukung apa yang disebut fungsi "panggilan balik". Jika antarmuka C menggunakan callback, Python yang setara sering perlu menyediakan mekanisme callback ke programmer Python; implementasi akan mengharuskan memanggil fungsi panggilan balik Python dari callback C. Kegunaan lain juga bisa dibayangkan.
Untungnya, interpreter Python mudah dipanggil secara rekursif, dan ada antarmuka standar untuk memanggil fungsi Python. (Saya tidak akan memikirkan bagaimana memanggil parser Python dengan string tertentu sebagai masukan - jika Anda tertarik, lihat implementasi -copsi baris perintahModules/main.cdari kode sumber Python.)
Memanggil fungsi Python itu mudah. Pertama, program Python entah bagaimana harus memberikan Anda objek fungsi Python. Anda harus menyediakan fungsi (atau beberapa antarmuka lain) untuk melakukan ini. Ketika fungsi ini dipanggil, simpan penunjuk ke objek fungsi Python (berhati-hatilah Py_INCREF()!) Dalam variabel global - atau di mana pun Anda mau. Misalnya, fungsi berikut mungkin menjadi bagian dari definisi modul:
static PyObject *my_callback = NULL;

static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
    PyObject *result = NULL;
    PyObject *temp;

    if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
        if (!PyCallable_Check(temp)) {
            PyErr_SetString(PyExc_TypeError, "parameter must be callable");
            return NULL;
        }
        Py_XINCREF(temp);         /* Add a reference to new callback */
        Py_XDECREF(my_callback);  /* Dispose of previous callback */
        my_callback = temp;       /* Remember new callback */
        /* Boilerplate to return "None" */
        Py_INCREF(Py_None);
        result = Py_None;
    }
    return result;
}
Fungsi ini harus terdaftar dengan penerjemah menggunakan METH_VARARGSbendera; ini dijelaskan dalam bagian Tabel Metode dan Fungsi Inisialisasi Modul . The PyArg_ParseTuple()fungsi dan argumen didokumentasikan dalam bagian Mengekstrak Parameter dalam Fungsi Ekstensi .
Makro Py_XINCREF()dan Py_XDECREF()kenaikan / pengurangan jumlah referensi dari suatu objek dan aman di hadapan pointer NULL (tetapi perhatikan bahwa temp tidak akan NULL dalam konteks ini). Info lebih lanjut tentang mereka di bagian Referensi Hitung .
Kemudian, ketika saatnya untuk memanggil fungsi tersebut, Anda memanggil fungsi C PyObject_CallObject()Fungsi ini memiliki dua argumen, baik pointer ke objek Python arbitrer: fungsi Python, dan daftar argumen. Daftar argumen harus selalu berupa objek tuple, yang panjangnya adalah jumlah argumen. Untuk memanggil fungsi Python tanpa argumen, lewat NULL, atau tupel kosong; untuk menyebutnya dengan satu argumen, lewati tuple tunggal. Py_BuildValue()mengembalikan tuple ketika string formatnya terdiri dari nol atau lebih kode format di antara tanda kurung. Sebagai contoh:
int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
PyObject_CallObject()mengembalikan pointer objek Python: ini adalah nilai kembali dari fungsi Python. PyObject_CallObject()adalah "referensi-hitungan-netral" sehubungan dengan argumennya. Dalam contoh, sebuah tuple baru dibuat untuk melayani sebagai daftar argumen, yang Py_DECREF()langsung dilakukan setelah PyObject_CallObject()panggilan.
Nilai kembalinya PyObject_CallObject()adalah "baru": apakah itu adalah objek baru, atau itu adalah objek yang ada yang jumlah referensi telah bertambah. Jadi, kecuali Anda ingin menyimpannya dalam variabel global, Anda harus entah bagaimana Py_DECREF()hasilnya, bahkan (terutama!) Jika Anda tidak tertarik dengan nilainya.
Sebelum Anda melakukan ini, bagaimanapun, penting untuk memeriksa bahwa nilai kembaliannya bukan NULL . Jika ya, fungsi Python dihentikan dengan memunculkan eksepsi. Jika kode C yang dipanggil PyObject_CallObject()dipanggil dari Python, sekarang harus mengembalikan indikasi kesalahan ke pemanggil Python-nya, sehingga interpreter dapat mencetak jejak stack, atau kode Python panggilan dapat menangani pengecualian. Jika ini tidak mungkin atau tidak diinginkan, pengecualian harus dibersihkan dengan menelepon PyErr_Clear()Sebagai contoh:
if (result == NULL)
    return NULL; /* Pass error back */
...use result...
Py_DECREF(result);
Tergantung pada antarmuka yang diinginkan ke fungsi panggilan balik Python, Anda mungkin juga harus memberikan daftar argumen PyObject_CallObject()Dalam beberapa kasus daftar argumen juga disediakan oleh program Python, melalui antarmuka yang sama yang ditentukan fungsi panggilan balik. Ini kemudian dapat disimpan dan digunakan dengan cara yang sama seperti objek fungsi. Dalam kasus lain, Anda mungkin harus membuat tupel baru untuk dilewatkan sebagai daftar argumen. Cara termudah untuk melakukan ini adalah menelepon Py_BuildValue()Misalnya, jika Anda ingin menyampaikan kode acara integral, Anda dapat menggunakan kode berikut:
PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
    return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
Perhatikan penempatan Py_DECREF(arglist)segera setelah panggilan, sebelum pemeriksaan kesalahan! Juga perhatikan bahwa dengan tegas kode ini tidak lengkap: Py_BuildValue()mungkin kehabisan memori, dan ini harus diperiksa.
Anda juga dapat memanggil fungsi dengan argumen kata kunci dengan menggunakan PyObject_Call(), yang mendukung argumen dan argumen kata kunci. Seperti pada contoh di atas, kita gunakan Py_BuildValue()untuk menyusun kamus.
PyObject *dict;
...
dict = Py_BuildValue("{s:i}", "name", val);
result = PyObject_Call(my_callback, NULL, dict);
Py_DECREF(dict);
if (result == NULL)
    return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);

1.7. Ekstrak Parameter dalam Fungsi Ekstensi 

The PyArg_ParseTuple()Fungsi dinyatakan sebagai berikut:
int PyArg_ParseTuple(PyObject *arg, char *format, ...);
The arg argumen harus menjadi objek tuple yang berisi daftar argumen dilewatkan dari Python ke fungsi C. The Format Argumen harus format string, yang sintaks dijelaskan dalam argumen Parsing dan nilai-nilai bangunan dalam Manual Referensi API Python / C. Argumen yang tersisa harus berupa alamat variabel yang jenisnya ditentukan oleh string format.
Perhatikan bahwa ketika PyArg_ParseTuple()memeriksa bahwa argumen Python memiliki tipe yang diperlukan, tidak dapat memeriksa validitas alamat dari variabel C yang dikirimkan ke panggilan: jika Anda membuat kesalahan di sana, kode Anda mungkin akan crash atau setidaknya menimpa bit acak dalam memori. Jadi hati-hati!
Perhatikan bahwa referensi obyek Python yang disediakan untuk pemanggil adalah referensi yang dipinjam ; jangan mengurangi jumlah referensi mereka!
Beberapa contoh panggilan:
int ok;
int i, j;
long k, l;
const char *s;
int size;

ok = PyArg_ParseTuple(args, ""); /* No arguments */
    /* Python call: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* A string */
    /* Possible Python call: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */
    /* Possible Python call: f(1, 2, 'three') */
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
    /* A pair of ints and a string, whose size is also returned */
    /* Possible Python call: f((1, 2), 'three') */
{
    const char *file;
    const char *mode = "r";
    int bufsize = 0;
    ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
    /* A string, and optionally another string and an integer */
    /* Possible Python calls:
       f('spam')
       f('spam', 'w')
       f('spam', 'wb', 100000) */
}
{
    int left, top, right, bottom, h, v;
    ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
             &left, &top, &right, &bottom, &h, &v);
    /* A rectangle and a point */
    /* Possible Python call:
       f(((0, 0), (400, 300)), (10, 10)) */
}
{
    Py_complex c;
    ok = PyArg_ParseTuple(args, "D:myfunction", &c);
    /* a complex, also providing a function name for errors */
    /* Possible Python call: myfunction(1+2j) */
}

1,8. Parameter Kata Kunci untuk Fungsi Ekstensi 

The PyArg_ParseTupleAndKeywords()Fungsi dinyatakan sebagai berikut:
int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,
                                char *format, char *kwlist[], ...);
The arg dan Format parameter identik dengan orang-orang dari PyArg_ParseTuple()fungsi. The kwdict parameter kamus kata kunci diterima sebagai parameter ketiga dari runtime Python. The kwlist parameter adalah NULL daftar -terminated string yang mengidentifikasi parameter; nama-nama dicocokkan dengan informasi jenis dari format dari kiri ke kanan. Pada kesuksesan, PyArg_ParseTupleAndKeywords()mengembalikan nilai true, jika tidak mengembalikan salah dan memunculkan eksepsi yang sesuai.
Catatan

Nested tuples tidak dapat diuraikan ketika menggunakan argumen kata kunci! Parameter kata kunci yang dilewatkan yang tidak ada dalamkwlist akan menyebabkan TypeErrordinaikkan.
Berikut ini contoh modul yang menggunakan kata kunci, berdasarkan contoh oleh Geoff Philbrick ( philbrick @ hks . Com ):
#include "Python.h"

static PyObject *
keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)
{
    int voltage;
    char *state = "a stiff";
    char *action = "voom";
    char *type = "Norwegian Blue";

    static char *kwlist[] = {"voltage", "state", "action", "type", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,
                                     &voltage, &state, &action, &type))
        return NULL;

    printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",
           action, voltage);
    printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);

    Py_INCREF(Py_None);

    return Py_None;
}

static PyMethodDef keywdarg_methods[] = {
    /* The cast of the function is necessary since PyCFunction values
     * only take two PyObject* parameters, and keywdarg_parrot() takes
     * three.
     */
    {"parrot", (PyCFunction)keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,
     "Print a lovely skit to standard output."},
    {NULL, NULL, 0, NULL}   /* sentinel */
};
void
initkeywdarg(void)
{
  /* Create the module and add the functions */
  Py_InitModule("keywdarg", keywdarg_methods);
}

1.9. Membangun Nilai Sewenang-wenang 

Fungsi ini adalah lawannya PyArg_ParseTuple()Ini dinyatakan sebagai berikut:
PyObject *Py_BuildValue(char *format, ...);
Ini mengenali satu set unit format yang mirip dengan yang dikenal oleh PyArg_ParseTuple(), tetapi argumen (yang merupakan input ke fungsi, bukan output) tidak harus pointer, hanya nilai. Ini mengembalikan objek Python baru, cocok untuk kembali dari fungsi C yang disebut dari Python.
Satu perbedaan dengan PyArg_ParseTuple(): sementara yang terakhir membutuhkan argumen pertama untuk menjadi tuple (karena daftar argumen Python selalu direpresentasikan sebagai tupel secara internal), Py_BuildValue()tidak selalu membangun tuple. Ini membangun tuple hanya jika string formatnya berisi dua atau lebih unit format. Jika string format kosong, itu mengembalikan Nonejika berisi persis satu unit format, ia mengembalikan objek apa pun yang dijelaskan oleh unit format tersebut. Untuk memaksanya mengembalikan ukuran tupel 0 atau satu, sisipkan string format.
Contoh (di sebelah kiri panggilan, di sebelah kanan nilai Python yang dihasilkan):
Py_BuildValue("")                        None
Py_BuildValue("i", 123)                  123
Py_BuildValue("iii", 123, 456, 789)      (123, 456, 789)
Py_BuildValue("s", "hello")              'hello'
Py_BuildValue("ss", "hello", "world")    ('hello', 'world')
Py_BuildValue("s#", "hello", 4)          'hell'
Py_BuildValue("()")                      ()
Py_BuildValue("(i)", 123)                (123,)
Py_BuildValue("(ii)", 123, 456)          (123, 456)
Py_BuildValue("(i,i)", 123, 456)         (123, 456)
Py_BuildValue("[i,i]", 123, 456)         [123, 456]
Py_BuildValue("{s:i,s:i}",
              "abc", 123, "def", 456)    {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)",
              1, 2, 3, 4, 5, 6)          (((1, 2), (3, 4)), (5, 6))

1.10. Jumlah Referensi 

Dalam bahasa seperti C atau C ++, programmer bertanggung jawab untuk alokasi dinamis dan dealokasi memori pada heap. Di C, ini dilakukan menggunakan fungsi malloc()dan free()Di C ++, operator newdan deletedigunakan dengan makna yang pada dasarnya sama dan kami akan membatasi diskusi berikut ke c case.
Setiap blok memori yang dialokasikan dengan malloc()akhirnya harus dikembalikan ke kolam memori yang tersedia dengan tepat satu panggilan ke free()Penting untuk menelepon free()pada waktu yang tepat. Jika alamat blok dilupakan tetapi free()tidak dipanggil, memori yang ditempati tidak dapat digunakan kembali sampai program berakhir. Ini disebut kebocoran memori . Di sisi lain, jika sebuah program panggilan free()untuk blok dan kemudian terus menggunakan blok, itu menciptakan konflik dengan penggunaan kembali blok melalui malloc()panggilan lain Ini disebut menggunakan memori yang dibebaskan . Ini memiliki konsekuensi buruk yang sama seperti mereferensikan data yang tidak diinisialisasi - core dump, hasil yang salah, crash misterius.
Penyebab umum kebocoran memori adalah jalur yang tidak biasa melalui kode. Misalnya, suatu fungsi dapat mengalokasikan blok memori, melakukan beberapa perhitungan, dan kemudian membebaskan blok lagi. Sekarang perubahan dalam persyaratan untuk fungsi dapat menambahkan tes ke perhitungan yang mendeteksi kondisi kesalahan dan dapat kembali sebelum waktunya dari fungsi. Sangat mudah untuk lupa untuk membebaskan blok memori yang dialokasikan saat mengambil keluar prematur ini, terutama ketika ditambahkan nanti ke kode. Kebocoran seperti itu, sekali diperkenalkan, sering tidak terdeteksi untuk waktu yang lama: kesalahan keluar hanya diambil dalam sebagian kecil dari semua panggilan, dan kebanyakan mesin modern memiliki banyak memori virtual, sehingga kebocoran hanya menjadi jelas dalam proses yang lama berjalan. yang sering menggunakan fungsi bocor. Karena itu,
Karena Python sangat sering menggunakan malloc()dan free()itu membutuhkan strategi untuk menghindari kebocoran memori serta penggunaan memori yang bebas. Metode yang dipilih disebut referensi penghitungan . Prinsipnya sederhana: setiap objek berisi counter, yang bertambah ketika referensi ke objek disimpan di suatu tempat, dan yang dikurangi ketika referensi untuk itu dihapus. Ketika penghitung mencapai nol, referensi terakhir ke objek telah dihapus dan objek tersebut dibebaskan.
Strategi alternatif disebut pengumpulan sampah otomatis . (Kadang-kadang, penghitungan referensi juga disebut sebagai strategi pengumpulan sampah, maka saya menggunakan "otomatis" untuk membedakan keduanya.) Keuntungan besar dari pengumpulan sampah otomatis adalah bahwa pengguna tidak perlu memanggil free()secara eksplisit. (Keuntungan lain yang diklaim adalah peningkatan kecepatan atau penggunaan memori - ini bukan fakta yang keras.) Kerugiannya adalah bahwa untuk C, tidak ada kolektor sampah otomatis yang benar-benar portabel, sementara penghitungan referensi dapat diimplementasikan secara mudah (selama fungsi malloc() dan free()tersedia - yang dijamin oleh Standar C). Mungkin suatu hari pengumpul sampah otomatis yang cukup portabel akan tersedia untuk C. Sampai saat itu, kita harus hidup dengan jumlah referensi.
Sementara Python menggunakan implementasi penghitungan referensi tradisional, itu juga menawarkan detektor siklus yang berfungsi untuk mendeteksi siklus referensi. Ini memungkinkan aplikasi untuk tidak khawatir tentang membuat referensi melingkar langsung atau tidak langsung; ini adalah kelemahan pengumpulan sampah yang dilaksanakan hanya dengan menggunakan penghitungan referensi. Siklus referensi terdiri dari objek yang mengandung (mungkin tidak langsung) referensi untuk dirinya sendiri, sehingga setiap objek dalam siklus memiliki jumlah referensi yang tidak nol. Penerapan penghitungan referensi tipikal tidak dapat merebut kembali memori milik objek apa pun dalam siklus referensi, atau direferensikan dari objek dalam siklus, meskipun tidak ada referensi lebih lanjut untuk siklus itu sendiri.
Detektor siklus dapat mendeteksi siklus sampah dan dapat mengambil kembali mereka selama tidak ada finalizer yang diimplementasikan dengan Python ( __del__()metode). Ketika ada finalisasi seperti itu, detektor memaparkan siklus melalui gcmodul (khususnya, garbagevariabel dalam modul itu). The gcModul juga menyebabkan cara untuk menjalankan detektor (yang collect()fungsi), serta antarmuka konfigurasi dan kemampuan untuk menonaktifkan detektor pada saat runtime. Detektor siklus dianggap sebagai komponen opsional; meskipun ini termasuk secara default, itu dapat dinonaktifkan pada saat membangun menggunakan --without-cycle-gcopsi untuk mengkonfigurasi skrip pada platform Unix (termasuk Mac OS X) atau dengan menghapus definisi WITH_CYCLE_GCdipyconfig.htajuk di platform lain. Jika detektor siklus dinonaktifkan dengan cara ini, gc modul tidak akan tersedia.

1.10.1. Referensi Menghitung dengan Python 

Ada dua makro, Py_INCREF(x)dan Py_DECREF(x), yang menangani penambahan dan pengurangan jumlah referensi. Py_DECREF()juga membebaskan objek saat hitungan mencapai nol. Untuk fleksibilitas, itu tidak memanggil free()secara langsung - melainkan, membuat panggilan melalui pointer fungsi dalam objek jenis objek . Untuk tujuan ini (dan lainnya), setiap objek juga berisi pointer ke objek jenisnya.
Pertanyaan besar sekarang tetap: kapan harus digunakan Py_INCREF(x)dan Py_DECREF(x)Mari kenalkan dulu beberapa istilah. Tidak ada yang "memiliki" sebuah objek; namun, Anda dapat memiliki referensi ke suatu objek. Jumlah referensi objek sekarang didefinisikan sebagai jumlah referensi yang dimiliki untuk itu. Pemilik referensi bertanggung jawab untuk menelepon Py_DECREF()ketika referensi tidak lagi diperlukan. Kepemilikan referensi dapat ditransfer. Ada tiga cara untuk membuang referensi yang dimiliki: berikan, simpan, atau hubungi Py_DECREF()Lupa membuang referensi yang dimiliki menyebabkan kebocoran memori.
Juga dimungkinkan untuk meminjam [2] referensi ke suatu objek. Peminjam referensi tidak boleh menelepon Py_DECREF()Peminjam tidak boleh berpegang pada objek lebih lama dari pemiliknya yang dipinjam. Menggunakan referensi yang dipinjam setelah pemilik telah membuangnya risiko menggunakan memori yang dibebaskan dan harus dihindari sepenuhnya [3] .
Keuntungan dari meminjam memiliki referensi adalah bahwa Anda tidak perlu berhati-hati membuang referensi pada semua jalur yang mungkin melalui kode - dengan kata lain, dengan referensi pinjaman Anda tidak menjalankan risiko bocor ketika keluar prematur diambil. Kerugian dari pemilikan atas kepemilikan adalah bahwa ada beberapa situasi halus dimana dalam kode yang tampaknya benar, referensi yang dipinjam dapat digunakan setelah pemilik dari mana pinjaman tersebut dipinjamkan sebenarnya telah membuangnya.
Referensi yang dipinjam dapat diubah menjadi referensi yang dimiliki dengan menelepon Py_INCREF()Ini tidak mempengaruhi status pemilik dari mana referensi dipinjam - itu menciptakan referensi milik baru, dan memberikan tanggung jawab pemilik penuh (pemilik baru harus membuang referensi dengan benar, serta pemilik sebelumnya).

Komentar