เป็นไปได้หรือไม่ที่จะรวบรวม Python เป็นรหัสเครื่อง


128

เป็นไปได้แค่ไหนที่จะรวบรวม Python (อาจผ่านการแทนค่า C ระดับกลาง) ลงในรหัสเครื่อง

สันนิษฐานว่าจำเป็นต้องเชื่อมโยงไปยังไลบรารีรันไทม์ของ Python และส่วนใด ๆ ของไลบรารีมาตรฐาน Python ซึ่งเป็น Python เองก็จำเป็นต้องคอมไพล์ (และเชื่อมโยง) ด้วย

นอกจากนี้คุณจะต้องรวมตัวแปล Python หากคุณต้องการทำการประเมินนิพจน์แบบไดนามิก แต่อาจเป็นชุดย่อยของ Python ที่ไม่อนุญาตให้ทำสิ่งนี้ก็ยังมีประโยชน์

จะให้ข้อได้เปรียบด้านความเร็วและ / หรือการใช้งานหน่วยความจำหรือไม่? สันนิษฐานว่าเวลาเริ่มต้นของตัวแปล Python จะถูกตัดออก (แม้ว่าไลบรารีที่ใช้ร่วมกันจะยังคงต้องโหลดเมื่อเริ่มต้น)


2
Btw คำถามของคุณจะชัดเจนกว่า IMHO หากคุณขอ "รหัสเครื่อง" แทนที่จะเป็นรหัสวัตถุ
Torsten Marek

ลองหลาม→ 11l → c ++ transpiler
tav

คำตอบ:


31

ลองใช้คอมไพเลอร์ShedSkin Python-to-C ++ แต่มันยังห่างไกลจากความสมบูรณ์แบบ นอกจากนี้ยังมี Psyco - Python JIT หากต้องการเพียง speedup เท่านั้น แต่ IMHO นี่ไม่คุ้มกับความพยายาม สำหรับส่วนที่มีความสำคัญอย่างรวดเร็วของโค้ดทางออกที่ดีที่สุดคือการเขียนเป็นส่วนขยาย C / C ++


5
FYI, ShedSkin ยกเลิกการรองรับ Windows
sorin

2
@sorin: วันนี้มันรองรับ windows ... code.google.com/p/shedskin/downloads/…

2
ที่ดีที่สุดของการแก้ปัญหา speedwise ยังคงอาจจะPyPy
Cees Timmerman

หนังหุ้มหนังไม่มีงานทำมาประมาณสองปีแล้ว :(
Perkins

53

ดังที่ @Greg Hewgill กล่าวไว้มีเหตุผลที่ดีที่ทำให้สิ่งนี้ไม่สามารถทำได้เสมอไป อย่างไรก็ตามรหัสบางประเภท (เช่นรหัสอัลกอริทึมมาก) สามารถเปลี่ยนเป็นรหัสเครื่อง "จริง" ได้

มีหลายตัวเลือก:

  • ใช้Psycoซึ่งแสดงรหัสเครื่องแบบไดนามิก คุณควรเลือกอย่างรอบคอบว่าจะแปลงวิธีการ / ฟังก์ชันใด
  • ใช้Cythonซึ่งเป็นภาษาคล้าย Python ที่รวบรวมเป็นส่วนขยาย Python C
  • ใช้PyPyซึ่งมีตัวแปลจาก RPython ( ชุดย่อยที่ถูก จำกัดของ Python ที่ไม่รองรับคุณสมบัติ "ไดนามิก" ส่วนใหญ่ของ Python) ไปที่ C หรือ LLVM
    • PyPy ยังคงอยู่ในการทดลองสูง
    • ไม่ใช่ส่วนขยายทั้งหมดที่จะมีอยู่

หลังจากนั้นคุณสามารถใช้หนึ่งในแพ็คเกจที่มีอยู่ (ตรึง, Py2exe, PyInstaller) เพื่อรวมทุกอย่างไว้ในไบนารีเดียว

สรุป: ไม่มีคำตอบทั่วไปสำหรับคำถามของคุณ หากคุณมีโค้ด Python ที่มีความสำคัญต่อประสิทธิภาพให้พยายามใช้ฟังก์ชันในตัวให้มากที่สุด (หรือถามคำถาม "ฉันจะทำให้โค้ด Python เร็วขึ้นได้อย่างไร") หากไม่ได้ผลให้ลองระบุรหัสและพอร์ตไปที่ C (หรือ Cython) แล้วใช้ส่วนขยาย


3
Pypy เป็นผู้สืบทอด Psyco
bcattle

19

py2c ( https://github.com/pradyun/Py2C ) สามารถแปลงรหัส python เป็น c / c ++ ฉันเป็นผู้พัฒนาเดี่ยวของ py2c


ดูเหมือนเครื่องมือที่มีประโยชน์ ยังคงได้รับการบำรุงรักษาอยู่หรือไม่?
Anderson Green

@AndersonGreen มันอยู่ในขั้นตอนการพัฒนาขั้นต้นในครั้งสุดท้ายที่ฉันทำมัน (อาจจะคล้ายกันในตอนนี้) ฉันออกจากโครงการแล้วเพราะฉันขี้เกียจ หากคุณไม่สังเกตเห็นข้อความ "สำคัญ" แสดงว่าตอนนี้ย้ายไปที่ GitHub แล้ว
Ramchandra Apte

ลิงก์ชี้ไปยังโปรแกรมติดตั้งที่ไม่ได้รับการติดตั้งซึ่งดูเหมือนจะเป็นโครงการอื่น py2c ยังมีอยู่ใน GitHub หรือไม่?
Anderson Green

@AndersonGreen ว้าวที่ไม่มีใครสังเกตเห็นมานาน! จัดให้เลย
Ramchandra Apte

ลิงก์บนcode.google.com/p/py2cยังคงชี้ไปยังโปรแกรมติดตั้งที่ยังไม่เสร็จสมบูรณ์ดังนั้นจึงจำเป็นต้องอัปเดตทันที
Anderson Green

15

PyPyเป็นโครงการที่นำ Python มาใช้งานใน Python อีกครั้งโดยใช้การคอมไพล์กับโค้ดเนทีฟเป็นหนึ่งในกลยุทธ์การใช้งาน (อื่น ๆ ที่เป็น VM พร้อม JIT โดยใช้ JVM เป็นต้น) เวอร์ชัน C ที่คอมไพล์แล้วทำงานช้ากว่า CPython โดยเฉลี่ย แต่เร็วกว่ามากสำหรับบางโปรแกรม

Shedskinเป็นคอมไพเลอร์ Python-to-C ++ รุ่นทดลอง

Pyrexเป็นภาษาที่ออกแบบมาเป็นพิเศษสำหรับการเขียนโมดูลส่วนขยาย Python ได้รับการออกแบบมาเพื่อเชื่อมช่องว่างระหว่างโลกที่สวยงามระดับสูงและใช้งานง่ายของ Python กับโลกระดับต่ำที่ยุ่งเหยิงของ C


3
Cython เป็นส้อมที่เป็นมิตรกับ Pyrex ที่ใช้กันอย่างแพร่หลายและได้รับการพัฒนามากขึ้น
Mike Graham

"โลกที่สวยงามระดับสูงและใช้งานง่ายของ Python และโลกที่ยุ่งเหยิงระดับต่ำของ C" - ตลกดีฉันแค่คิดว่า C และแอสเซมเบลอร์นั้น "ดี" และเรียบง่ายแค่ไหนและ Python ก็อาศัยอยู่ใน " โลกที่ยุ่งเหยิง "," ระดับสูง "
Reversed Engineer


10

สิ่งนี้อาจดูสมเหตุสมผลในตอนแรกอย่างไรก็ตามมีหลายสิ่งธรรมดาใน Python ที่ไม่สามารถแมปกับการเป็นตัวแทน C ได้โดยตรงโดยไม่ต้องใช้การสนับสนุนรันไทม์ Python จำนวนมาก ตัวอย่างเช่นเป็ดพิมพ์อยู่ในใจ ฟังก์ชั่นมากมายใน Python ที่อ่านอินพุตสามารถใช้ไฟล์หรืออ็อบเจกต์คล้ายไฟล์ได้ตราบเท่าที่รองรับการทำงานบางอย่างเช่น อ่าน () หรือ readline () หากคุณคิดถึงสิ่งที่ต้องใช้ในการจับคู่การสนับสนุนประเภทนี้กับ C คุณจะเริ่มจินตนาการถึงสิ่งต่าง ๆ ที่ระบบรันไทม์ของ Python ทำอยู่แล้ว

มียูทิลิตี้เช่นpy2exeที่จะรวมโปรแกรม Python และรันไทม์ไว้ในไฟล์ปฏิบัติการเดียว (เท่าที่จะทำได้)


1
จะเกิดอะไรขึ้นถ้าเป้าหมายของฉันคือเพื่อให้แน่ใจว่าโค้ดคอมไพล์เนื่องจากภาษาที่คอมไพล์แบบคงที่ (อย่างน้อยก็ในความคิดของฉัน) มีโอกาสน้อยที่จะระเบิดในขณะทำงาน เป็นไปได้หรือไม่ที่จะระบุว่าfoo.xนิพจน์บางอย่างจะไม่ทำงานเนื่องจากfooจะไม่มีxในเวลาที่เรียก มีตัวตรวจสอบโค้ดแบบคงที่สำหรับ Python หรือไม่? Python สามารถคอมไพล์เป็น. Net assembly ...
Hamish Grubijan

10

Pyrexเป็นชุดย่อยของภาษา Python ที่รวบรวมเป็นภาษา C โดยคนที่สร้างความเข้าใจรายการสำหรับ Python เป็นครั้งแรก ส่วนใหญ่ได้รับการพัฒนาสำหรับการสร้างเครื่องห่อหุ้ม แต่สามารถใช้ในบริบททั่วไปได้ Cythonเป็นทางแยกของ pyrex ที่ได้รับการดูแลอย่างแข็งขันกว่า


2
Cython เป็นส้อมที่เป็นมิตรกับ Pyrex ที่ใช้กันอย่างแพร่หลายและได้รับการพัฒนามากขึ้น
Mike Graham

5

ข้อมูลอ้างอิงเพิ่มเติมบางส่วน:

  • https://github.com/dropbox/pystonเป็นคอมไพเลอร์ JIT สำหรับ Python ที่พัฒนาโดย Dropbox

  • http://pythran.readthedocs.io/เป็นตัวแปล Python เวลาคอมไพล์เป็น C ++ สำหรับการคำนวณทางวิทยาศาสตร์

  • https://github.com/cosmo-ethz/hopeเป็นตัวแปล JIT python เป็น C ++ สำหรับการคำนวณทางวิทยาศาสตร์


3

Jython มีคอมไพเลอร์ที่กำหนดเป้าหมาย JVM bytecode bytecode เป็นแบบไดนามิกอย่างเต็มที่เช่นเดียวกับภาษา Python เอง! เจ๋งมาก. (ใช่ตามที่คำตอบของ Greg Hewgill กล่าวถึงไบต์โค้ดจะใช้รันไทม์ Jython ดังนั้นไฟล์ Jython jar จะต้องถูกแจกจ่ายพร้อมกับแอพของคุณ)


2

Psycoเป็นคอมไพเลอร์แบบ Just-in-time (JIT): ไดนามิกคอมไพเลอร์สำหรับ Python รันโค้ดได้เร็วขึ้น 2-100 เท่า แต่ต้องการหน่วยความจำมาก

กล่าวโดยย่อ: เรียกใช้ซอฟต์แวร์ Python ที่คุณมีอยู่ได้เร็วขึ้นมากโดยไม่มีการเปลี่ยนแปลงในซอร์สของคุณ แต่จะไม่คอมไพล์เป็นรหัสออบเจ็กต์แบบเดียวกับที่คอมไพเลอร์ C ทำ


2

คำตอบคือ "ใช่เป็นไปได้" คุณสามารถใช้รหัส Python และพยายามรวบรวมเป็นรหัส C ที่เทียบเท่าโดยใช้ CPython API อันที่จริงเคยมีโครงการ Python2C ที่ทำแบบนั้น แต่ฉันไม่เคยได้ยินเกี่ยวกับเรื่องนี้มาหลายปีแล้ว (ย้อนกลับไปใน Python 1.5 วันคือเมื่อฉันเห็นครั้งสุดท้าย)

คุณสามารถลองแปลโค้ด Python เป็น C แบบเนทีฟให้ได้มากที่สุดและถอยกลับไปใช้ CPython API เมื่อคุณต้องการคุณสมบัติ Python จริง ฉันเคยคิดถึงความคิดนั้นด้วยตัวเองเมื่อเดือนหรือสองเดือนที่แล้ว อย่างไรก็ตามมันเป็นงานที่แย่มากและคุณสมบัติของ Python จำนวนมหาศาลนั้นยากมากที่จะแปลเป็น C: ฟังก์ชันซ้อนกันเครื่องกำเนิดไฟฟ้าอะไรก็ได้นอกจากคลาสง่ายๆด้วยวิธีการง่ายๆอะไรก็ตามที่เกี่ยวข้องกับการปรับเปลี่ยนโมดูล globals จากภายนอกโมดูลเป็นต้น ฯลฯ


2

สิ่งนี้ไม่ได้รวบรวม Python เข้ากับรหัสเครื่อง แต่อนุญาตให้สร้างไลบรารีที่ใช้ร่วมกันเพื่อเรียกรหัส Python

หากสิ่งที่คุณกำลังมองหาคือวิธีง่ายๆในการเรียกใช้โค้ด Python จาก C โดยไม่ต้องอาศัย execp คุณสามารถสร้างห้องสมุดสาธารณะจากรหัสหลามห่อด้วยโทรไม่กี่หลามฝัง API แอปพลิเคชันนี้เป็นไลบรารีที่ใช้ร่วมกันซึ่งเป็น. เพื่อให้คุณสามารถใช้ในไลบรารี / แอปพลิเคชันอื่น ๆ ได้

นี่คือตัวอย่างง่ายๆที่สร้างไลบรารีที่ใช้ร่วมกันซึ่งคุณสามารถเชื่อมโยงกับโปรแกรม C ได้ ไลบรารีที่ใช้ร่วมกันรันโค้ด Python

ไฟล์ python ที่จะเรียกใช้คือ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 สิ่งต่างๆอาจเป็นไปโดยอัตโนมัติได้บ้าง ...

Nuitkaอาจเป็นประโยชน์

นอกจากนี้ยังมีnumbaแต่ทั้งคู่ไม่ได้ตั้งใจที่จะทำในสิ่งที่คุณต้องการอย่างแน่นอน การสร้างส่วนหัว C จากโค้ด Python เป็นไปได้ แต่ถ้าคุณระบุวิธีการแปลงประเภท Python เป็นประเภท C หรือสามารถสรุปข้อมูลนั้นได้ ดูpython astroidสำหรับเครื่องวิเคราะห์ Python ast

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.