编译你的 Python 代码

提到 Python,或许你会想到,这是一门解释型的语言,确实是,但是也不是。首先区分一点,从形式上来说,Python 是一门语言,但是他有很多种实现(implementation),你可以参考这份清单,其中最著名的当然是 CPython ,这门语言的参考实现,他是解释执行代码的。但是理论上,他也可以有编译型的实现,这就是我今天要介绍的,他叫 Nuitka

Nuitka Logo

安装

Nuitka 的安装方式非常简单,他是一个普通的 Python Package,你可以从 pypi.org 下载。

1
2
3
pip install nuitka

python -m nuitka --help

不过,要正确编译你的代码,还需要一些额外的软件:

  • 一个 C 语言的编译器,gcc 或者 clang
  • CPython,2.6,2.7 或者 3.3 以上。

官方还没有支持 macOS

接下来我会用 docker 来演示,使用 python:3.7 镜像

1
docker run --rm python:3.7 bash

使用

我准备了这样一个程序,他用来计算斐波那契数列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# app.py
import sys
import time

from fab import fab


if __name__ == '__main__':
try:
n = int(sys.argv[1])
except IndexError:
n = 30

start_time = time.time()
result = fab(n)
end_time = time.time()
print(f'fab({n}) = {result}')
print(f'take {end_time - start_time:.3f} seconds')
1
2
3
4
5
6
7
8
# fab.py
def fab(n):
if n < 2:
return n
return fab(n - 1) + fab(n - 2)

if __name__ == '__main__':
print(f'fab(30) = {fab(30)}')

先试一下用 CPython 执行

1
2
3
python app.py
# fab(30) = 832040
# take 0.429 seconds

编译整个程序

如果你想编译整个程序,执行下面这条指令

1
python -m nuitka --portable app.py

这行命令执行完毕,我们会得到一个文件夹叫做 app.dist 它包含了所有需要的东西,你可以把它拷贝到另一台机器上执行,即使那台机器上没有安装 Python

1
2
3
4
cd app.dist
./app.exe
# fab(30) = 832040
# take 0.245 seconds

编译一个模块/包

Nuitka 也可以编译一个模块

1
2
python -m nuitka --module fab.py
# produce fab.so

你可以在 CPython 中导入编译后的模块

1
2
3
4
5
6
>>> from fab import fab
>>> fab
<compiled_function fab at 0x7efe9fa0f3c8>
>>> fab(30)
832040
>>>

Why

兼容性。Nuitka 和其他类似的项目的目标不太一样,他更关注与 CPython 之间的兼容性,Nuitka 的测试用例就来自 CPython。只要你的代码可以在 CPython 下面执行,用 Nuitka 编译出来的代码也应该正常执行。

便于分发。如果你打算用 Python 开发一个客户端应用,那么你要如何交付给你的用户呢?告诉他们,先去 python.org 下载安装 Python XXX 版本,然后下载你的程序,然后执行 pip install…? 这肯定不是一个好主意。如果可以的话当然是只需要下载程序,然后双击执行。Nuitka 提供了这样的一个可能性。

加速。因为是编译成机器代码,相比解释执行,减少了一些不必要的 overhead,比如说解析源代码,生成 bytecode。另外,因为编译成机器码的缘故,Nuitka 还可以做一些优化,作者声称一般能增加两倍的执行速度,所有需要做的只是切换到 Nuitka。

混淆源代码。或许你要分发给你的用户,但是有不想把源代码给他们。

以上