将Python编译为机器代码是否可行?


128

将Python(可能通过中间C表示形式)编译为机器代码的可行性如何?

大概需要链接到Python运行时库,并且Python标准库中Python本身的任何部分也需要进行编译(并链接)。

另外,如果您想对表达式进行动态求值,则需要捆绑Python解释器,但是也许不允许这样做的Python子集仍然有用。

它会提供任何速度和/或内存使用优势吗?大概可以省去Python解释器的启动时间(尽管共享库在启动时仍需要加载)。


2
顺便说一句,如果您要的是“机器代码”而不是目标代码,那么您的问题将会更加清楚。
Torsten Marek

Answers:


31

尝试使用ShedSkin从 Python到C ++的编译器,但这远非完美。如果仅需要加速,则还有Psyco-Python JIT。但是恕我直言,这不值得付出努力。对于速度至关重要的代码部分,最好的解决方案是将它们编写为C / C ++扩展。


5
仅供参考,ShedSkin放弃了Windows支持。
sorin 2010年

2
@sorin:好,今天它支持Windows ... code.google.com/p/shedskin/downloads/...

2
最好的解决方案仍然是PyPy
Cees Timmerman 2012年

棚皮大约两年没有做任何工作。:(
珀金斯

53

就像@Greg Hewgill所说的那样,有很多理由说明为什么这并不总是可能的。但是,某些类型的代码(例如非常算法的代码)可以变成“真实的”机器代码。

有几种选择:

  • 使用Psyco,它可以动态发出机器代码。不过,您应该仔细选择要转换的方法/函数。
  • 使用Cython,这是一种类似 Python的语言,已编译为Python C扩展
  • 使用PyPy,它具有从RPython(Python 的受限子集,不支持Python的某些“最动态”功能)到C或LLVM的转换器。
    • PyPy仍处于实验阶段
    • 并非所有扩展名都存在

之后,您可以使用现有的软件包之一(freeze,Py2exe,PyInstaller)将所有内容放入一个二进制文件中。

总而言之:您的问题没有一般性的答案。如果您具有对性能至关重要的Python代码,请尝试使用尽可能多的内置功能(或询问“如何使我的Python代码更快”问题)。如果那没有帮助,请尝试识别代码并将其移植到C(或Cython)并使用扩展名。


3
Pypy是Psyco的继任者
bcattle 2014年

19

py2c(https://github.com/pradyun/Py2C)可以将python代码转换为c / c ++我是py2c的开发人员。


这看起来是个有用的工具。仍在维护吗?
安德森·格林

@AndersonGreen这是我上次进行此开发时的早期开发阶段(现在可能与此类似)。我已经离开了项目,因为我很懒。如果您没有注意到“重要”文本,则该文本现已移至GitHub。
Ramchandra Apte 2014年

该链接指向unvanquished-installer,它似乎是一个不同的项目。py2c在GitHub上仍然可用吗?
安德森·格林

@AndersonGreen哇好久没被注意到了!
Ramchandra Apte 2014年

code.google.com/p/py2c上的链接仍指向unvanquished-installer,因此需要立即更新。
安德森·格林

15

PyPy是一个在Python中重新实现Python的项目,使用对本机代码的编译作为实现策略之一(其他方法是具有JIT的VM,使用JVM等)。他们的编译C版本平均运行速度比CPython慢​​,但对于某些程序,运行速度要快得多。

Shedskin是一个实验性的Python到C ++编译器。

Pyrex是专门用于编写Python扩展模块的语言。它旨在弥合Python的精美,高级,易于使用的世界与C的混乱,低级的世界之间的鸿沟。


3
Cython是Pyrex使用更广泛,更积极开发的友好型分叉。
Mike Graham 2012年

“ Python的漂亮,高级,易于使用的世界,以及C的混乱,低级的世界” –有趣的是,我只是在思考C和汇编程序如何“好”而简单,而Python生活在“混乱”,“高级”世界
逆向工程师

14

Nuitka是与libpython链接的Python到C ++编译器。这似乎是一个相对较新的项目。作者声称在pystone基准上比CPython 有了速度上的改进


10

乍看起来,这似乎是合理的,但是在Python中,有很多普通的东西不能直接映射到C表示形式,而又不会继承很多Python运行时支持。例如,想到鸭式打字。Python中的许多读取输入的函数可以获取一个文件或类似文件的文件对象,只要它支持某些操作即可。read()或readline()。如果您考虑将这种类型的支持映射到C会花费什么,那么您将开始完全想象Python运行时系统已经完成的各种工作。

有诸如py2exe之类的实用程序,它将Python程序和运行时捆绑到一个可执行文件中(尽可能)。


1
如果我的目标是确保代码可编译(因为至少在我看来,静态编译的语言在运行时不会崩溃),该怎么办?是否有可能确定一些foo.x表达不会起作用,因为foo不会有x在它被调用的时候。Python是否有任何静态代码检查器?可以将Python编译为.Net程序集……
Hamish Grubijan 2012年

10

Pyrex是可编译为C的Python语言的子集,由最初为Python 建立列表理解的人完成。它主要是为建筑包装纸开发的,但可以在更广泛的范围内使用。 Cython是派热克斯(pyrex)维护得更积极的分支。


2
Cython是Pyrex使用更广泛,更积极开发的友好型分叉。
Mike Graham


3

Jython有一个针对JVM字节码的编译器。字节码是完全动态的,就像Python语言本身一样!很酷。(是的,正如Greg Hewgill的答案所暗示的那样,字节码确实使用了Jython运行时,因此Jython jar文件必须随您的应用程序一起分发。)


2

普斯科是一种即时(JIT)编译器:适用于Python的动态编译器,代码运行速度快2到100倍,但是需要大量内存。

简而言之:它可以更快地运行现有的Python软件,而无需更改源代码,但是它无法像C编译器那样编译为目标代码。


2

答案是“是,有可能”。您可以使用Python代码,并尝试使用CPython API将其编译为等效的C代码。实际上,过去曾经有一个Python2C项目做到了这一点,但多年来我一直没有听说过它(在Python 1.5天内是我最后一次看到它的时候。)

您可以尝试将Python代码尽可能多地转换为本机C,然后在需要实际的Python功能时退回到CPython API。我最近一两个月一直在想这个主意。但是,这需要进行大量工作,并且大量Python功能很难转换为C:嵌套函数,生成器,除带有简单方法的简单类之外的任何东西,涉及从模块外部修改模块全局变量的任何东西,等等。等


2

这不会将Python编译为机器代码。但是允许创建一个共享库来调用Python代码。

如果您正在寻找的是一种不依赖execp的方式从C运行Python代码的简便方法。您可以从包装有几次对Python嵌入API的调用的python代码生成共享库。好了,该应用程序是一个共享库,一个.so,因此您可以在许多其他库/应用程序中使用。

这是一个创建共享库的简单示例,您可以将其与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,但是它们都不打算完全按照自己的意愿去做。可以从Python代码生成C标头,但前提是您指定如何将Python类型转换为C类型或可以推断出该信息。有关Python ast分析器,请参见python astroid

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.