سؤال

ما مدى جدوى تجميع بايثون (ربما عبر تمثيل C متوسط) في كود الآلة؟

من المفترض أنها ستحتاج إلى الارتباط بمكتبة وقت تشغيل Python، وأي أجزاء من مكتبة Python القياسية التي كانت Python نفسها ستحتاج إلى تجميعها (وربطها) أيضًا.

ستحتاج أيضًا إلى تجميع مترجم Python إذا كنت تريد إجراء تقييم ديناميكي للتعبيرات، ولكن ربما تظل مجموعة فرعية من Python لا تسمح بذلك مفيدة.

هل ستوفر أي مزايا تتعلق بالسرعة و/أو استخدام الذاكرة؟من المفترض أن يتم إلغاء وقت بدء تشغيل مترجم بايثون (على الرغم من أن المكتبات المشتركة ستظل بحاجة إلى التحميل عند بدء التشغيل).

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

المحلول

يحاول ShedSkin مترجم Python-to-C++، لكنه بعيد عن الكمال.يوجد أيضًا Psyco - Python JIT إذا كانت هناك حاجة إلى التسريع فقط.ولكن IMHO هذا لا يستحق كل هذا الجهد.بالنسبة للأجزاء ذات السرعة الحرجة من التعليمات البرمجية، فإن الحل الأفضل هو كتابتها كامتدادات C/C++.

نصائح أخرى

وكما يقول @Greg Hewgill، هناك أسباب وجيهة لعدم إمكانية تحقيق ذلك دائمًا.ومع ذلك، يمكن تحويل أنواع معينة من التعليمات البرمجية (مثل التعليمات البرمجية الخوارزمية للغاية) إلى تعليمات برمجية "حقيقية" للآلة.

هناك عدة خيارات:

  • يستخدم بسيكو, ، الذي يصدر كود الآلة ديناميكيًا.ومع ذلك، يجب عليك أن تختار بعناية الطرق/الوظائف التي تريد تحويلها.
  • يستخدم سايثون, ، وهو بايثون-يحب اللغة التي تم تجميعها في ملحق Python C
  • يستخدم PyPy, ، والذي يحتوي على مترجم من RPython (a مجموعة فرعية مقيدة من Python الذي لا يدعم بعض الميزات الأكثر "ديناميكية" في Python) إلى C أو LLVM.
    • لا يزال PyPy تجريبيًا للغاية
    • لن تكون كافة الملحقات موجودة

بعد ذلك، يمكنك استخدام إحدى الحزم الموجودة (تجميد، Py2exe، PyInstaller) لوضع كل شيء في ملف ثنائي واحد.

الكل في الكل:لا توجد إجابة عامة لسؤالك.إذا كان لديك كود Python الذي يعد أمرًا بالغ الأهمية للأداء، فحاول استخدام أكبر قدر ممكن من الوظائف المضمنة (أو اطرح سؤالاً "كيف يمكنني جعل كود Python الخاص بي أسرع").إذا لم يساعد ذلك، فحاول التعرف على الرمز ونقله إلى C (أو Cython) واستخدام الامتداد.

py2c ( http://code.google.com/p/py2c) يمكن تحويل رمز Python إلى C/C ++ أنا المطور المنفرد لـ PY2C.

نويتكا هو مترجم من Python إلى C++ يرتبط بـ libpython.ويبدو أنه مشروع جديد نسبيا.يدعي المؤلف أ تحسين السرعة عبر CPython على معيار pystone.

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

شيدسكين هو مترجم تجريبي لـ Python-to-C++.

بيركس هي لغة مصممة خصيصًا لكتابة وحدات امتداد Python.لقد تم تصميمه لسد الفجوة بين عالم Python الجميل وعالي المستوى وسهل الاستخدام وعالم C الفوضوي منخفض المستوى.

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

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

هناك مرافق مثل py2exe سيؤدي ذلك إلى تجميع برنامج Python ووقت التشغيل في ملف واحد قابل للتنفيذ (قدر الإمكان).

بعض المراجع الإضافية:

لدى Jython مترجم يستهدف كود JVM الثانوي.رمز البايت ديناميكي بالكامل، تمامًا مثل لغة بايثون نفسها!رائع جدا.(نعم، كما تشير إجابة Greg Hewgill، يستخدم الرمز الثانوي وقت تشغيل Jython، وبالتالي يجب توزيع ملف Jython jar مع تطبيقك.)

بسيكو هو نوع من المترجم في الوقت المناسب (JIT):يقوم المترجم الديناميكي لـ Python بتشغيل التعليمات البرمجية بشكل أسرع بمعدل 2 إلى 100 مرة، ولكنه يحتاج إلى قدر كبير من الذاكرة.

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

الجواب هو "نعم، هذا ممكن".يمكنك أخذ كود Python ومحاولة تجميعه في كود C المكافئ باستخدام CPython API.في الواقع، كان هناك مشروع Python2C يفعل ذلك بالضبط، لكنني لم أسمع عنه منذ سنوات عديدة (في أيام Python 1.5 عندما رأيته آخر مرة).

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

هذا لا يجمع بايثون إلى كود الآلة.ولكنه يسمح بإنشاء مكتبة مشتركة لاستدعاء كود بايثون.

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

فيما يلي مثال بسيط لإنشاء مكتبة مشتركة يمكنك ربطها ببرنامج C.تقوم المكتبة المشتركة بتنفيذ كود بايثون.

ملف بايثون الذي سيتم تنفيذه هو pythoncalledfromc.py:

# -*- encoding:utf-8 -*-
# this file must be named "pythoncalledfrom.py"

def main(string):  # args must a string
    print "python is called from c"
    print "string sent by «c» code is:"
    print string
    print "end of «c» code input"
    return 0xc0c4  # return something

يمكنك تجربتها مع python2 -c "import pythoncalledfromc; pythoncalledfromc.main('HELLO').سوف يخرج:

python is called from c
string sent by «c» code is:
HELLO
end of «c» code input

سيتم تعريف المكتبة المشتركة بما يلي بواسطة callpython.h:

#ifndef CALL_PYTHON
#define CALL_PYTHON

void callpython_init(void);
int callpython(char ** arguments);
void callpython_finalize(void);

#endif

ويرتبط callpython.c يكون:

// gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <python2.7/Python.h>

#include "callpython.h"

#define PYTHON_EXEC_STRING_LENGTH 52
#define PYTHON_EXEC_STRING "import pythoncalledfromc; pythoncalledfromc.main(\"%s\")"


void callpython_init(void) {
     Py_Initialize();
}

int callpython(char ** arguments) {
  int arguments_string_size = (int) strlen(*arguments);
  char * python_script_to_execute = malloc(arguments_string_size + PYTHON_EXEC_STRING_LENGTH);
  PyObject *__main__, *locals;
  PyObject * result = NULL;

  if (python_script_to_execute == NULL)
    return -1;

  __main__ = PyImport_AddModule("__main__");
  if (__main__ == NULL)
    return -1;

  locals = PyModule_GetDict(__main__);

  sprintf(python_script_to_execute, PYTHON_EXEC_STRING, *arguments);
  result = PyRun_String(python_script_to_execute, Py_file_input, locals, locals);
  if(result == NULL)
    return -1;
  return 0;
}

void callpython_finalize(void) {
  Py_Finalize();
}

يمكنك تجميعه باستخدام الأمر التالي:

gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so

قم بإنشاء ملف باسم callpythonfromc.c الذي يحتوي على ما يلي:

#include "callpython.h"

int main(void) {
  char * example = "HELLO";
  callpython_init();
  callpython(&example);
  callpython_finalize();
  return 0;
}

تجميعها وتشغيلها:

gcc callpythonfromc.c callpython.so -o callpythonfromc
PYTHONPATH=`pwd` LD_LIBRARY_PATH=`pwd` ./callpythonfromc

هذا مثال أساسي للغاية.يمكن أن ينجح الأمر، ولكن اعتمادًا على المكتبة، قد يكون من الصعب إجراء تسلسل لهياكل بيانات C إلى Python ومن Python إلى C.يمكن أتمتة الأمور إلى حد ما ...

نويتكا قد يكون مفيدا.

أيضا هناك نومبا لكن كلاهما لا يهدفان إلى فعل ما تريده بالضبط.من الممكن إنشاء رأس C من كود Python، ولكن فقط إذا قمت بتحديد كيفية تحويل أنواع Python إلى أنواع C أو يمكنك استنتاج تلك المعلومات.يرى بيثون أسترويد لمحلل بايثون ast.

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