什么是Python字节码文件

Python是一种解释性语言,在Python执行py脚本文件时,Python解释器将源码转换为字节码写入到内存,然后再由解释器来执行这些字节码。

而在使用import导入其他模块时,则会在模块的py文件旁边自动生成.pyc格式的同名文件,这就是该模块经过编译后生成的二进制字节码文件,优点是加载速度有所提升、实现源码隐藏、一定程度上防止反编译。

每次在导入该模块时,会自动检查其字节码文件的修改时间是否与自身一致,如果一致则直接从该字节码文件读取内容,否则该模块重新导入,并更新当前已有的字节码文件,这样修改模块源代码后,实际使用时也会自动更新。

除了import以外,也可以通过py_compile这个Python自带模块,实现批量生成.pyc文件。

import py_compile
py_compile.compile(r"/path/to/pyfile or pydir")

 

生成错误

最近在维护一份Python2.7版本的代码时,发现一直平稳运行的生产环境报错导入第三方模块错误import error,经过检查发现部分自动生成的.pyc字节码文件大小只有100+字节,正常情况下应该是略大于原始.py文件大小。

在删除该.pyc文件重新运行后,自动生成的新.pyc文件正常,生产环境得以平稳运行。

在搜寻资料后得知,在Python通过import导入py文件时,如果遇到I/O异常,会得到一个EOF标志,然后以所读取到的内容生成pyc文件,但这个过程并没有检查I/O异常。

在模块同时访问量太多、服务器资源紧张、带宽跑满等情况下会比较容易出现这个问题,而在Python3.X版本中修复了此bug,因此在Python2.X中需要多注意一点。

解决方法是通过以下几种方法禁止生成.pyc字节码文件,虽然加载速度会稍受影响,但考虑到稳定性是可以接受的。

 

设置环境变量

这个方法是较为常用的,也比较方便。

export PYTHONDONTWRITEBYTECODE=1

 

使用-B参数

在运行py脚本时加上-B参数

python -B test.py

 

使用dont_write_bytecode函数

在导入模块前写入以下代码

import sys
sys.dont_write_bytecode = True

 

反编译

前面说过pyc字节码文件只能一定程度上防反编译,其实只能防一下小白,通过uncompyle6这个第三方库就可以把pyc字节码文件完美反编译成py源文件,连注释都能保留。

首先安装uncompyle6。

pip install uncompyle6

然后在CMD窗口中运行以下命令

uncompyle6 -o test.py test.pyc

这样就可以将test.pyc字节码文件反编译成test.py源码文件了,如果报错找不到uncompyle6,就把python安装目录下的Scripts文件夹路径添加到系统的PATH环境变量里。


乞丐不会妒忌百万富翁,
但他肯定会妒忌收入更高的乞丐。

——罗素