Tahun lalu, ada kebutuhan untuk melengkapi proyek lama yang ditulis dalam C dengan fungsionalitas di Python3 . Terlepas dari kenyataan bahwa ada artikel tentang hal ini, saya menderita di tahun itu dan sekarang ketika saya menulis program untuk artikel itu. Oleh karena itu, saya akan memberikan contoh saya tentang cara bekerja dengan Python3 dari C di Linux (dengan apa yang saya gunakan). Saya akan menjelaskan cara membuat kelas dan memanggil metodenya, untuk mengakses variabel. Memanggil fungsi dan mendapatkan variabel dari modul. Dan juga masalah yang saya temui dan tidak bisa mengerti.
Paket
Kami menggunakan API Python standar untuk C. Paket Python yang diperlukan:
- python3
- python3-dev
- python3-all
- python3-all-dev
- libpython3-all-dev
Bekerja di penerjemah
Hal paling sederhana adalah memuat dan bekerja dalam interpreter Python.
Agar berfungsi, Anda harus menghubungkan file header:
#include <Python.h>
Memuat penerjemah:
Py_Initialize();
Berikutnya adalah blok kerja dengan Python, misalnya:
PyRun_SimpleString("print('Hello!')");
Bongkar penerjemah:
Py_Finalize();
Contoh lengkap:
#include <Python.h> void main() { // Python Py_Initialize(); // PyRun_SimpleString("print('Hello!')"); // Python Py_Finalize(); }
Cara mengompilasi dan menjalankan:
gcc simple.c $(python3-config --includes --ldflags) -o simple && ./simple Hello!
Tetapi ini tidak akan berhasil:
gcc $(python3-config --includes --ldflags) simple.c -o simple && ./simple /tmp/ccUkmq57.o: In function `main': simple.c:(.text+0x5): undefined reference to `Py_Initialize' simple.c:(.text+0x16): undefined reference to `PyRun_SimpleStringFlags' simple.c:(.text+0x1b): undefined reference to `Py_Finalize' collect2: error: ld returned 1 exit status
Ini karena python3-config --includes --ldflags berkembang menjadi seperti ini:
-I/usr/include/python3.6m -I/usr/include/python3.6m -L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.6m -lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
Di sini saya pikir urutan koneksi dari -WL linker adalah penting . Siapa tahu lebih tepatnya, tulis tentang ini dalam komentar, saya akan melengkapi jawabannya.
Penjelasan dari MooNDeaR :
Semuanya cukup sederhana - karakter dicari dalam satu pass dan semua karakter yang tidak digunakan dibuang. Jika Anda menempatkan simple.c di akhir, ternyata linker akan melihat penggunaan simbol Py_Initialize () setelah melihat pustaka python, semua simbol yang akan dibuang pada saat ini (karena mereka tidak digunakan).
Contoh memanggil fungsi dari file Python:
simple.c
#include <Python.h> void python() { // Python Py_Initialize(); // // sys PyRun_SimpleString("import sys"); // python PyRun_SimpleString("sys.path.append('./src/python')"); PyRun_SimpleString("import simple"); PyRun_SimpleString("print(simple.get_value(2))"); PyRun_SimpleString("print(simple.get_value(2.0))"); PyRun_SimpleString("print(simple.get_value(\"Hello!\"))"); // Python Py_Finalize(); } void main() { puts("Test simple:"); python(); }
simple.py
Tetapi ini adalah hal-hal sederhana dan tidak menarik, kami tidak mendapatkan hasil dari fungsinya.
Bekerja dengan fungsi dan variabel modul
Ini agak sulit.
Memuat interpreter Python dan modul func.py ke dalamnya:
PyObject * python_init() {
Melepaskan sumber interpreter Python:
void python_clear() {
Bekerja dengan variabel dan fungsi modul.
char * python_func_get_str(char *val) { char *ret = NULL;
Mari kita bahas ini lebih detail.
pVal = PyObject_CallFunction(pObjct, (char *) "(s)", val);
"(s)" berarti 1 parameter char * dilewatkan sebagai argumen untuk mendapatkan_nilai (x) . Jika kita perlu memberikan beberapa argumen ke fungsi, itu akan seperti ini:
pVal = PyObject_CallFunction(pObjct, (char *) "(sss)", val1, val2, val3);
Jika Anda perlu memasukkan int , maka huruf i akan digunakan, semua tipe data yang mungkin dan peruntukannya dapat ditemukan dalam dokumentasi Python.
pVal = PyObject_CallFunction(pObjct, (char *) "(i)", my_int);
func.py:
( Masalah teratasi )
Masalah yang saya temui dan belum bisa mengerti:
int main() { puts("Test func:"); if (!python_init()) { puts("python_init error"); return -1; } puts("Strings:"); printf("\tString: %s\n", python_func_get_str("Hello from Python!")); puts("Attrs:"); printf("\ta: %d\n", python_func_get_val("a")); printf("\tb: %d\n", python_func_get_val("b")); printf("\tc: %d\n", python_func_get_val("c")); python_clear(); return 0; }
Jika saya ingin mendapatkan b atau c dari func.py , maka pada:
Py_Finalize();
Saya mendapatkan kesalahan segmentasi . Tidak ada masalah dengan hanya mendapatkan a .
Saat menerima variabel kelas, tidak ada masalah juga.
Penjelasan dari pwl :
PyObject PyDict_GetItemString (PyObject p, const char * key)
Nilai pengembalian: Referensi yang dipinjam. Tidak ada yang perlu dilakukan untuk referensi yang dipinjam.
Masalahnya adalah saya memanggil Py_XDECREF () untuk PyDict_GetItemString () . Tidak perlu melakukan ini untuk fungsi ini, yang menyebabkan kesalahan segmentasi .
Pekerjaan kelas
Masih ada yang sedikit lebih rumit.
Memuat modul interpreter Python dan class.py ke dalamnya.
PyObject * python_init() {
Melewati string sebagai argumen dan mendapatkan string kembali
char * python_class_get_str(char *val) { char *ret = NULL; pVal = PyObject_CallMethod(pInstance, (char *) "get_value", (char *) "(s)", val); if (pVal != NULL) { PyObject* pResultRepr = PyObject_Repr(pVal);
Tidak ada masalah di sini, semuanya berfungsi tanpa kesalahan. Ada beberapa contoh dalam sumber bagaimana bekerja dengan int , double , bool .
Sambil menulis materi, saya menyegarkan kembali pengetahuan saya)
Semoga bermanfaat.
Referensi