بايثون من C (C API)

رئيسي

في العام الماضي ، كانت هناك حاجة لاستكمال مشروع قديم كتب في C مع وظائف في Python3 . على الرغم من وجود مقالات حول هذا الموضوع ، فقد عانيت في ذلك العام والآن عندما كتبت برامج لهذا المقال. لذلك ، سأقدم أمثلة عن كيفية العمل مع Python3 من C تحت Linux (مع ما استخدمته). سوف أصف كيفية إنشاء فصل واستدعاء أساليبه للوصول إلى المتغيرات. استدعاء الوظائف والحصول على المتغيرات من الوحدة النمطية. وكذلك المشاكل التي واجهتها ولم أستطع فهمها.


حزم


نستخدم API Python القياسي لـ C. حزم بيثون المطلوبة:


  • python3
  • python3 ديف
  • python3 للجميع
  • python3 بين جميع ديف
  • libpython3 بين جميع ديف

العمل في المترجم


أبسط شيء هو التحميل والعمل في مترجم Python.


للعمل ، يجب عليك توصيل ملف الرأس:


#include <Python.h> 

تحميل المترجم:


 Py_Initialize(); 

التالي هو كتلة من العمل مع بيثون ، على سبيل المثال:


 PyRun_SimpleString("print('Hello!')"); 

تفريغ المترجم:


 Py_Finalize(); 

مثال كامل:


 #include <Python.h> void main() { //   Python Py_Initialize(); //     PyRun_SimpleString("print('Hello!')"); //   Python Py_Finalize(); } 

كيفية تجميع وتشغيل:


 gcc simple.c $(python3-config --includes --ldflags) -o simple && ./simple Hello! 

ولكن هذا لن ينجح:


 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 

هذا لأن python3-config - يشمل - علامات الفلاش تتوسع في هذا النوع من الأشياء:


 -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 

أنا هنا أعتقد أن ترتيب اتصال رابط -Wl مهم . من يعرف أكثر دقة ، أكتب عن ذلك في التعليقات ، وسأكمل الإجابة.


شرح من MooNDeaR :


كل شيء بسيط للغاية - يتم البحث عن الأحرف في مسار واحد ويتم إلقاء جميع الأحرف غير المستخدمة. إذا قمت بوضع simple.c في النهاية ، فقد تبين أن الرابط سيرى استخدام رمز Py_Initialize () بعد أن ينظر إلى مكتبات python ، وسيتم تجاهل جميع رموزها في هذه اللحظة (لأنه لم يتم استخدامها).

مثال على استدعاء دالة من ملف 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


 #!/usr/bin/python3 #-*- coding: utf-8 -*- def get_value(x): return x 

ولكن هذه أشياء بسيطة وغير مثيرة للاهتمام ، ونحن لا نحصل على نتيجة لهذه الوظيفة.


العمل مع وظائف ومتغيرات الوحدة


هذا هو اصعب قليلا.
تحميل مترجم Python ووحدة func.py فيه:


 PyObject * python_init() { //   Python Py_Initialize(); do { //   sys sys = PyImport_ImportModule("sys"); sys_path = PyObject_GetAttrString(sys, "path"); //     Python folder_path = PyUnicode_FromString((const char*) "./src/python"); PyList_Append(sys_path, folder_path); //  func.py pName = PyUnicode_FromString("func"); if (!pName) { break; } //    pModule = PyImport_Import(pName); if (!pModule) { break; } //      pDict = PyModule_GetDict(pModule); if (!pDict) { break; } return pDict; } while (0); //   PyErr_Print(); } 

تحرير موارد مترجم Python:


 void python_clear() { //    Py_XDECREF(pDict); Py_XDECREF(pModule); Py_XDECREF(pName); Py_XDECREF(folder_path); Py_XDECREF(sys_path); Py_XDECREF(sys); //   Python Py_Finalize(); } 

العمل مع المتغيرات ووظائف الوحدة النمطية.


 /** *          */ char * python_func_get_str(char *val) { char *ret = NULL; //   get_value  func.py pObjct = PyDict_GetItemString(pDict, (const char *) "get_value"); if (!pObjct) { return ret; } do { //  pObjct  . if (!PyCallable_Check(pObjct)) { break; } pVal = PyObject_CallFunction(pObjct, (char *) "(s)", val); if (pVal != NULL) { PyObject* pResultRepr = PyObject_Repr(pVal); //     ,     Python   . //   pResultRepr     . ret = strdup(PyBytes_AS_STRING(PyUnicode_AsEncodedString(pResultRepr, "utf-8", "ERROR"))); Py_XDECREF(pResultRepr); Py_XDECREF(pVal); } else { PyErr_Print(); } } while (0); return ret; } /** *       int */ int python_func_get_val(char *val) { int ret = 0; //     val pVal = PyDict_GetItemString(pDict, (const char *) val); if (!pVal) { return ret; } //    long if (PyLong_Check(pVal)) { ret = _PyLong_AsInt(pVal); } else { PyErr_Print(); } return ret; } 

دعونا نتناول هذا بمزيد من التفصيل.


 pVal = PyObject_CallFunction(pObjct, (char *) "(s)", val); 

"(s)" تعني أنه يتم تمرير معلمة 1 char * كوسيطة إلى get_value (x) . إذا احتجنا إلى تمرير العديد من الوسيطات إلى الوظيفة ، فستكون هكذا:


 pVal = PyObject_CallFunction(pObjct, (char *) "(sss)", val1, val2, val3); 

إذا كنت بحاجة لتمرير كثافة العمليات ، فسيتم استخدام الحرف الأول ، ويمكن العثور على جميع أنواع البيانات الممكنة وتسمياتها في وثائق Python.


 pVal = PyObject_CallFunction(pObjct, (char *) "(i)", my_int); 

func.py:


 #!/usr/bin/python3 #-*- coding: utf-8 -*- a = 11 b = 22 c = 33 def get_value(x): return x def get_bool(self, x): if x: return True else: return False 

(تم حل المشكلة )
المشكلة التي واجهتها ولم أستطع فهمها بعد:


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

إذا كنت أرغب في الحصول على b أو c من func.py ، فقم بما يلي:


 Py_Finalize(); 

أحصل على خطأ تجزئة . لا توجد مشكلة من هذا القبيل مع الحصول على فقط.
عند تلقي متغيرات الفصل ، لا توجد مشاكل أيضًا.


شرح من pwl :


PyObject PyDict_GetItemString (PyObject p، const char * key)
قيمة الإرجاع: إشارة المقترضة. لا شيء يجب القيام به للإشارة إلى المقترضة.

كانت المشكلة أنني كنت أتصل بـ Py_XDECREF () من أجل PyDict_GetItemString () . ليس من الضروري القيام بذلك لهذه الوظيفة ، مما يؤدي إلى خطأ تجزئة .


العمل الطبقي


لا يزال هناك أكثر تعقيدا قليلا.
قم بتحميل مترجم Python و class.py في ذلك.


 PyObject * python_init() { //   Python Py_Initialize(); do { //   sys sys = PyImport_ImportModule("sys"); sys_path = PyObject_GetAttrString(sys, "path"); //     Python folder_path = PyUnicode_FromString((const char*) "./src/python"); PyList_Append(sys_path, folder_path); //  Unicode   UTF-8  pName = PyUnicode_FromString("class"); if (!pName) { break; } //   class pModule = PyImport_Import(pName); if (!pModule) { break; } //      pDict = PyModule_GetDict(pModule); if (!pDict) { break; } //   Class  class.py pClass = PyDict_GetItemString(pDict, (const char *) "Class"); if (!pClass) { break; } //  pClass  . if (!PyCallable_Check(pClass)) { break; } //   Class pInstance = PyObject_CallObject(pClass, NULL); return pInstance; } while (0); //   PyErr_Print(); } 

تمرير سلسلة كوسيطة واستعادة السلسلة مرة أخرى


 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); //     ,     Python   . ret = strdup(PyBytes_AS_STRING(PyUnicode_AsEncodedString(pResultRepr, "utf-8", "ERROR"))); Py_XDECREF(pResultRepr); Py_XDECREF(pVal); } else { PyErr_Print(); } return ret; } 

لم تكن هناك مشاكل هنا ، كل شيء يعمل دون أخطاء. هناك أمثلة في المصدر حول كيفية التعامل مع int ، مزدوج ، منطقي .


أثناء كتابة المواد قمت بتحديث معرفتي)
آمل أن يكون مفيدا.


مراجع


Source: https://habr.com/ru/post/ar466181/


All Articles