Bob's Blog

Web开发、测试框架、自动化平台、APP开发、机器学习等

返回上页首页

理解Python中的字节码



当我们运行python脚本时,会在脚本文件对应的目录下产生一个文件夹__pycache__,文件夹里会有.pyc为后缀的文件,这些就是存储了python语句的字节码。接下来回顾一下字节码的作用、生成、运行、反汇编。

字节码的作用

当python文件被当做模块导入时,才会产生字节码文件。因为少了从语句解释到字节码的过程,只是做字节码的执行是会快一点的,当下次再次运行时,只要没有修改过原py文件的内容,则会直接执行pyc文件中的字节码,以提高效率。否则将再次生成新的pyc文件。

字节码记录的是一个个操作指令,而不是机器能识别的二进制。

指定生成字节码文件

我们也可以指定生成字节码文件。

比如先创建文件夹test,并创建bc.py并输入print("hello").

我们可以如下生成字节码:

import py_compile
py_compile.compile("./test/bc.py")

# 或者用compileall
import compileall
compileall.compile_file("./test/bc.py")

# 或者指定文件夹
import compileall
compileall.compile_dir("./test")

字节码文件的运行

该pyc文件是可以直接运行的:

$ python ./__pycache__/bc.cpython-38.pyc
hello

但是当需要import非标准库的py文件时,直接运行pyc文件会报错,需要修改对应的pyc文件名。

比如现在有两个文件,a.py和b.py:

# a.py
print("in file a")

# b.py
import os
import a
print(os.getcwd())

用compileall来直接生成两个文件的字节码文件。

此时运行python ./__pycache__/b.cpython-38.pyc时是会报错的,提示在import a时遇到了ModuleNotFoundError。

此时把a.cpython-38.pyc改成a.pyc即可,再次运行就不会出现找不到模块的错误了。

不过如果b.py中没有import a的语句,则不会报错。

反汇编字节码

如果需要知道具体有哪些字节码的指令,可以用dis。比如下面是在命令行里执行的,查看数字相加的字节码,能看到加载值两次,执行相加,并返回值:

>>> def add_num(a, b):
...     return a+b
>>> import dis
>>> dis.dis(add_num)
  2           0 LOAD_FAST                0 (a)
              2 LOAD_FAST                1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE

也正因为查看机器码是如此的简单,而且从pyc重新转换回py文件可以用uncompyle来做到,因此如果发布只打包pyc文件仍然是不安全的。另外pyc往往依赖于解释器的版本,如果换一个python版本则有可能无法正常运行。

下一篇:  Python代码的加密和混淆
上一篇:  Python的解释器为何不叫虚拟机

共有0条评论

添加评论

暂无评论