سؤال

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

IIRC، كان أحد اختصاصات Python الأصلية بمثابة لغة نماذج أولية، إلا أن Python ليبرالية جدًا في السماح بتمرير الوظائف والوظائف والكائنات إلى الوظائف والأساليب، في حين أظن أن الأمر نفسه ليس صحيحًا بالنسبة لـ C أو Fortran.

ما الذي يجب أن أعرفه عن تصميم الوظائف/الفئات التي أتصور أنها يجب أن تتفاعل مع اللغة المترجمة؟وما حجم هذه المشكلات المحتملة التي يتم التعامل معها من خلال مكتبات مثل cTypes وbgen و جرعة كبيرة, Boost.Python, سايثون أو بايثون SIP?

بالنسبة لحالة الاستخدام المحددة هذه (مكتبة مناسبة) أتخيل السماح للمستخدمين بتعريف الوظائف الرياضية (Guassian وLorentzian وما إلى ذلك) كوظائف Python والتي يمكن بعد ذلك تمريرها وتفسيرها بواسطة مكتبة تركيب التعليمات البرمجية المجمعة.يعد تمرير المصفوفات وإرجاعها أمرًا ضروريًا أيضًا.

هل كانت مفيدة؟

المحلول

أخيرًا سؤال يمكنني حقًا تقديم إجابة قيمة له :).

لقد قمت بالتحقيق في f2py وboost.python وswig وcython وpyrex في عملي (دكتوراه في تقنيات القياس البصري).لقد استخدمت swig على نطاق واسع، وboost.python البعض، وpyrex وcython كثيرًا.لقد استخدمت أيضًا أنواع ctypes.هذا هو تقسيمي:

تنصل:هذه هي تجربتي الشخصية.أنا لا أشارك في أي من هذه المشاريع.

جرعة كبيرة:لا يلعب بشكل جيد مع C++.ينبغي أن يكون الأمر كذلك، ولكن مشاكل تغيير الأسماء في خطوة الربط كانت بمثابة صداع كبير بالنسبة لي على Linux وMac OS X.إذا كان لديك كود C وتريد ربطه بـ python، فهذا حل جيد.لقد قمت بتغليف GTS لتلبية احتياجاتي وأحتاج إلى كتابة مكتبة C مشتركة يمكنني الاتصال بها.أنا لن أنصح به.

أنواع C:لقد كتبت غلاف libdc1394 (مكتبة كاميرا IEEE) باستخدام ctypes وكانت تجربة واضحة للغاية.يمكنك العثور على الكود على https://launchpad.net/pydc1394.يتطلب تحويل الرؤوس إلى تعليمات برمجية بيثون الكثير من العمل، ولكن بعد ذلك يعمل كل شيء بشكل موثوق.هذه طريقة جيدة إذا كنت تريد ربط مكتبة خارجية.توجد Ctypes أيضًا في stdlib لـ python، بحيث يمكن للجميع استخدام الكود الخاص بك على الفور.هذه أيضًا طريقة جيدة للتلاعب بـ lib جديد في لغة python بسرعة.يمكنني أن أوصي به للتفاعل مع libs الخارجية.

Boost.Python:ممتع جدا.إذا كان لديك كود C++ خاص بك بالفعل وتريد استخدامه في بايثون، فانتقل إلى هذا.من السهل جدًا ترجمة هياكل فئة c++ إلى هياكل فئة python بهذه الطريقة.أوصي به إذا كان لديك كود c++ الذي تحتاجه في بيثون.

بيركس/سايثون: استخدم سايثون وليس بيركس.فترة.Cython أكثر تقدمًا وأكثر متعة في الاستخدام.في الوقت الحاضر، أفعل كل شيء باستخدام cython الذي كنت أفعله باستخدام SWIG أو Ctypes.إنها أيضًا أفضل طريقة إذا كان لديك كود بايثون يعمل ببطء شديد.العملية رائعة تماما:يمكنك تحويل وحدات python الخاصة بك إلى وحدات cython، وإنشاءها والاستمرار في التنميط والتحسين كما لو كانت python (لا يلزم تغيير الأدوات).يمكنك بعد ذلك تطبيق نفس القدر (أو القليل) من كود C المختلط مع كود بايثون الخاص بك.يعد هذا أسرع بكثير من الاضطرار إلى إعادة كتابة أجزاء كاملة من تطبيقك بلغة C؛أنت فقط تعيد كتابة الحلقة الداخلية.

التوقيتات:ctypes لديها أعلى حمل استدعاء (~ 700ns)، يليه Boost.python (322ns)، ثم مباشرة بواسطة swig (290ns).يتمتع Cython بأقل قدر من المكالمات العامة (124ns) وأفضل ردود الفعل حيث يقضي وقتًا في (دعم cProfile!).الأرقام مأخوذة من صندوقي الذي يستدعي دالة تافهة تُرجع عددًا صحيحًا من غلاف تفاعلي؛وبالتالي، لا يتم توقيت الحمل الزائد لاستيراد الوحدة النمطية، بل يتم تحديد الحمل الزائد لاستدعاء الوظيفة فقط.لذلك، من الأسهل والأكثر إنتاجية الحصول على كود بايثون بسرعة عن طريق إنشاء ملفات تعريف واستخدام سايثون.

ملخص:لمشكلتك، استخدم Cython ;).آمل أن تكون هذه المتهدمة مفيدة لبعض الناس.سأجيب بكل سرور على أي سؤال متبقي.


يحرر:وأنسى أن أذكر:ولأغراض رقمية (أي الاتصال بـ NumPy)، استخدم Cython؛لديهم الدعم لذلك (لأنهم يطورون السايثون بشكل أساسي لهذا الغرض).لذلك ينبغي أن يكون هذا إجراء 1+ آخر لقرارك.

نصائح أخرى

لم أستخدم SWIG أو SIP، لكني أجد كتابة أغلفة Python باستخدام Boost.python أن تكون قوية جدًا وسهلة الاستخدام نسبيًا.

لست واضحًا بشأن متطلباتك لتمرير الأنواع بين C/C++ وpython، ولكن يمكنك القيام بذلك بسهولة إما عن طريق تعريض نوع C++ إلى python، أو باستخدام عام دفعة::بيثون::كائن وسيطة لواجهة برمجة تطبيقات C++ الخاصة بك.يمكنك أيضًا تسجيل المحولات لتحويل أنواع python تلقائيًا إلى أنواع C++ والعكس.

إذا كنت تخطط لاستخدام Boost.python، فإن درس تعليمي مكان جيد للبدء.

لقد قمت بتنفيذ شيء مشابه إلى حد ما لما تحتاجه.لدي وظيفة C ++ تقبل وظيفة Python وصورة كوسائط ، وتطبق وظيفة Python على كل بكسل في الصورة.

Image* unary(boost::python::object op, Image& im)
{
    Image* out = new Image(im.width(), im.height(), im.channels());
    for(unsigned int i=0; i<im.size(); i++)
    {
        (*out)[i] == extract<float>(op(im[i]));
    }
    return out;
}

في هذه الحالة، الصورة عبارة عن كائن C++ مكشوف لـ python (صورة ذات بكسلات عائمة)، وop هي دالة محددة بواسطة python (أو في الحقيقة أي كائن python ذو سمة __call__).يمكنك بعد ذلك استخدام هذه الوظيفة على النحو التالي (بافتراض وجود الأحادي في الصورة المطلوبة التي تحتوي أيضًا على صورة ووظيفة تحميل):

import image
im = image.load('somefile.tiff')
double_im = image.unary(lambda x: 2.0*x, im)

أما بالنسبة لاستخدام المصفوفات مع التعزيز، فأنا شخصيًا لم أفعل ذلك، لكنني أعلم أن وظيفة تعريض المصفوفات للبايثون باستخدام التعزيز متاحة - هذا قد يكون مفيدا.

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

سيوفر هذا تعيينًا واحدًا لواحد من كود نموذج بايثون الخاص بك إلى الكود المترجم في نهاية المطاف، وسيتيح لك استخدام com.ctypes بسهولة وتجنب مجموعة كاملة من الصداع.

للحصول على ذروة الملاءمة، ستحتاج بالتأكيد إلى استخدام المصفوفات، الأمر الذي سيؤدي إلى تعقيد الأمور قليلاً، ولكنه لا يزال قابلاً للتنفيذ باستخدام أنواع ctypes.

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

لمعرفة ما تفعله، قد ترغب أيضًا في التحقق منه NumPy, ، والتي قد تقوم ببعض الأعمال التي قد ترغب في دفعها إلى لغة C، بالإضافة إلى العرض بعض المساعدة الإضافية في نقل البيانات ذهابًا وإيابًا بين Python وC.

f2py (جزء من numpy) هو بديل أبسط لـ SWIG وboost.python لتغليف كود تحليل الأرقام C/Fortran.

من خلال تجربتي، هناك طريقتان سهلتان لاستدعاء كود C من كود Python.هناك طرق أخرى، كلها مزعجة و/أو مطولة.

الأول والأسهل هو تجميع مجموعة من أكواد C كمكتبة مشتركة منفصلة ثم استدعاء الوظائف في تلك المكتبة باستخدام ctypes.لسوء الحظ، فإن تمرير أي شيء آخر غير أنواع البيانات الأساسية ليس بالأمر الهين.

الطريقة الثانية الأسهل هي كتابة وحدة Python في لغة C ثم استدعاء الوظائف في تلك الوحدة.يمكنك تمرير أي شيء تريده إلى وظائف C هذه دون الحاجة إلى القفز عبر أي عقبات.ومن السهل استدعاء وظائف أو أساليب Python من وظائف C هذه، كما هو موضح هنا: https://docs.python.org/extending/extending.html#calling-python-functions-from-c

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

تعد لغة Python ليبرالية جدًا في السماح بتمرير الوظائف والوظائف والكائنات إلى الوظائف والأساليب، في حين أظن أن الأمر نفسه لا ينطبق على لغة C أو Fortran على سبيل المثال.

في لغة C، لا يمكنك تمرير دالة كوسيطة إلى دالة، ولكن يمكنك تمرير مؤشر دالة لا تقل جودة عن الدالة.

لا أعرف مدى فائدة ذلك عندما تحاول دمج كود C وPython ولكني أردت فقط توضيح مفهوم خاطئ واحد.

بالإضافة إلى الأدوات المذكورة أعلاه، يمكنني أن أوصي باستخدام بيركس (لإنشاء وحدات امتداد بايثون) أو بسيكو (كمترجم JIT لبيثون).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top