编译Python源代码


类型:Python,创建时间:Jan. 1, 2012, 5:05 p.m.

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/63/。

在日常生活中,Python代码一般是不编译的,几个py文件复制来就能用。再加上脚本语言的名头,有些不太了解Python的朋友就以为Python没有编译这个过程。其实,虽然Python是脚本语言,但它与Java和C#一样,只能执行字节码。只是Python将编译过程隐藏起来,不大明显而已。今天这篇笔记详细记述一下Python的编译过程以及一些技巧。

我们一般使用这样的命令运行一个python程序:

C:\> python im.py

其中,im.py文件的内容是:

#!/usr/bin/env python
import mymodule
mymodule.say("hello")

执行这一命令后,Python解释器其实并不一定会读入mymodule.py文件,它会尝试读取mymodule.pyc文件或者mymodule.pyo文件。如果都失败了,或者mymodule.py文件比mymodule.pyc/mymodule.pyo新,才转而读入mymodule.py文件。Python只懂得解释执行字节码,所以mymodule.py文件读入后第一件事就是先进行编译。编译的结果会被尝试保存到mymodule.pyc文件中。等一下会讨论如何控制Python的编译过程。

虽然与C++/Java这样的静态语言一样,采用了编译(compile)这个术语,实际情况还是有所不同的。首先,最明显的一点,Python编译的最终结果不是机器码,而是字节码。Python的编译实际上主要是进行文法分析,生成一个抽象语法树,然后转储成字节码形式了事。

从上面的介绍可以发现Python的编译与C++相去甚远。不过倒与Java有些相像,因为它们都生成字节码。凭良心说,Java的编译过程比Python先进很多。Java的解释器在执行Java字节码的时候,会使用JIT,将循环操作等热点转化成为机器码。所以有时候Java的性能能够达到接近C++的级别。Python缺少JIT并非故意所为,而是缺少人力资源。现在已经有一个pysco的外部模块,据说能大大提高Python代码的速度,不过这个模块还没有进入Python的官方代码。

Python的字节码列表可以见此链接:http://www.python.org/doc/2.5.2/lib/bytecodes.html

接下来,介绍一下如何"稍微"控制Python的编译过程。只所以说是稍微,是因为无论何种情况,Python都会对字节码进行一些简单优化(basic Peephole Optimization,详情参见Python的源代码。2.5版本的Python位于 Python/compile.c,2.7版本则位于Python/peephole.c)。这些优化不能通过环境变量或者命令参数将其禁用。比如:

if True:
    return 1
else:
    return 0

会被优化成为:

return 1

更多的优化正被添加到Python源代码内。只有三个参数能影响编译时的优化:

  1. 去除所有的assert语句,并将__debug__这个内置变量的值设置为False。方法是运行Python的时候在命令行添加参数:

    python -O im.py
    
  2. 除了第一条所做的事情,还去除所有docstring。方法是运行Python的时候在命令行添加参数:

    python -OO im.py
    
  3. 默认的,对于一个模块,编译后的字节码会被保存到与源代码相同的文件夹内。这样就可以加速模块的载入速度。大多数使用Python的朋友们都写过包含两三个文件的程序。通常可以发现除了.py文件之外,文件夹内还会有.pyc文件。mymodule.pyc文件即是mymodule.py的字节码。如果运行Python的命令行包含了-O或者-OO参数,Python会将优化后的字节码保存到mymodule.pyo文件。想要禁止生成.pyc或者.pyo文件,可以在运行Python的时候,在命令行里添加参数:

    python -B im.py
    

    还可以设置环境变量:

    c:\> set PYTHONDONTWRITEBYTECODE=x
    

看完上面三条说明,有的朋友可能会疑问,"-O"和"-OO"参数真的就干那三件事?不幸的是,还真是如此。至少在Python2.5的时候就是这样。所以加"-O"参数并不能明显优化Python的运行速度。这两个选项的真正作用是区分调试版本和发行版本。在程序中可以尽量多增加一些assert语句,以便程序员在调试阶段发现一些隐藏的错误。而在发布时,将这些语句去除。如果你的软件是商业软件的话,加上"-OO"参数可以让别人看不清内部函数的用途,增加一些破解的难度。有了这些,谁还说Python不能写商业软件?

说到商业软件,不得不说到反编译工具。很神奇的一件事,Python内置了反编译的模块!囧rz,使用方法也很简单:

>>> import mymodule
>>> import dis
>>> dis.dis(mymodule) #打印出反编译的结果

反编译后的代码与汇编语言接近,所以想要破解Python商业软件还是有一定难度的。据说还有一些工具能够反编译出漂亮的Python源代码,我并没有亲见。

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/63/。


暂时还没有任何评论。


何不来发表一下您对本文的看法(使用Markdown语法,分段空两行):