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

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

据推测,它需要链接到一个 Python 运行时库,而 Python 标准库的任何部分本身就是 Python,也需要编译(并链接到)。

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

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

106534 次浏览

Psyco 是一种即时(JIT)编译器: Python 的动态编译器,运行代码快2-100倍,但需要很多内存。

简而言之: 它运行现有的 Python 软件要快得多,源代码没有任何变化,但它不会像 C 编译器那样编译成目标代码。

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

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

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

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

Pyrex 是专门为编写 Python 扩展模块而设计的语言。它的设计目的是为了在优秀的、高级的、易于使用的 Python 世界和混乱的、低级的 C 世界之间架起一座桥梁。

正如@Greg Hewgill 所说,有充分的理由说明这并不总是可能的。然而,某些类型的代码(如非常算法代码)可以转换成“真正的”机器代码。

有几种选择:

  • 使用动态发送机器代码的 神经病。但是,您应该仔细选择要转换的方法/函数。
  • 使用 Cython,它是一种 Python-喜欢语言,被编译成 Python C 扩展
  • 使用 PyPy,它有一个从 RPython (Python 的 受限子集,它不支持 Python 的一些最“动态”的特性)到 C 或 LLVM 的转换器。
    • PyPy 还在高度实验阶段
    • 并非所有的分机都会出现

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

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

Pyrex 是编译成 C 的 Python 语言的子集,由第一个为 Python 构建 列表理解法的家伙完成。它主要是为构建包装而开发的,但是可以在更一般的上下文中使用。Cython是一个更积极维护耐热分叉。

答案是“是的,这是可能的”。您可以使用 Python 代码并尝试使用 CPythonAPI 将其编译为等效的 C 代码。事实上,曾经有一个 Python 2C 项目就是这样做的,但是我已经很多年没有听说过它了(回到 Python 1.5天是我最后一次看到它的时候)

您可以尝试尽可能多地将 Python 代码翻译成原生 C,然后在需要实际的 Python 特性时回过头来使用 CPythonAPI。过去的一两个月里,我自己也在考虑这个想法。然而,这是一项非常繁重的工作,而且大量的 Python 特性很难转换成 C 语言: 嵌套函数、生成器、除了带有简单方法的简单类以外的任何东西,任何涉及从模块外部修改模块全局的东西,等等。

Nuitka 是一个 Python 到 C + + 的编译器,它链接到 libpython。这似乎是一个相对较新的项目。作者声称在 pystone 基准上有一个 提高速度优于 CPython。

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

如果您正在寻找的是一种从 C 运行 Python 代码的简单方法,而不需要依赖 Execp 内容。您可以通过几个对 Python 嵌入 API的调用从 python 代码中生成一个共享库。应用程序是一个共享库。以便您可以在许多其他库/应用程序中使用。

下面是一个创建共享库的简单示例,您可以链接到一个 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 可能会有帮助。

也有 笨蛋,但他们都不打算做什么,你想确切。从 Python 代码生成 C 头是可能的,但前提是您必须指定如何将 Python 类型转换为 C 类型,或者可以推断出该信息。有关 Python ast 分析器,请参见 巨蟒星

一些额外的参考资料: