-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c056555
commit 2bae5e6
Showing
4 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# CPython的虚拟机\n", | ||
"\n", | ||
"CPython 使用一个基于栈的虚拟机.也就是说它完全面向栈数据结构的(你可以\"推入(push)\"一个东西到栈顶,或者从栈顶上\"弹出(pop)\"一个东西来).\n", | ||
"\n", | ||
"CPython 使用三种类型的栈:\n", | ||
"\n", | ||
"+ 调用栈(call stack)\n", | ||
"\n", | ||
" 这是运行 Python 程序的主要结构.它为每个当前活动的函数调用使用了一个东西--\"帧(frame)\".栈底是程序的入口点.每个函数调用推送一个新的帧到调用栈,每当函数调用返回后,这个帧被销毁.\n", | ||
" \n", | ||
"+ 计算栈(evaluation stack)\n", | ||
"\n", | ||
" 也称为数据栈(data stack).在每个帧中有一个计算栈.这个栈就是 Python 函数运行的地方.运行的 Python 代码大多数是由推入到这个栈中的东西组成的,这个栈负责操作它们.然后在返回后销毁它们。\n", | ||
"\n", | ||
"+ 块栈(block stack)\n", | ||
"\n", | ||
" 在每个帧中还有一个块栈.它被 Python 用于去跟踪某些类型的控制结构--循环、`try / except 块`、以及 `with 块`,全部推入到块栈中,当你退出这些控制结构时块栈被销毁.这将帮助 Python 了解任意给定时刻哪个块是活动的.比如,一个 continue 或者 break 语句可能影响正确的块.\n", | ||
" \n", | ||
" \n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.6.4" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 2 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# 编译与反编译\n", | ||
"\n", | ||
"首先说明下本文的python特指官方的Cpython实现.\n", | ||
"\n", | ||
"python外界的定义是解释型语言,其原理是将代码放入解释器,解释一句执行一句.然而实际上python的执行流程会比这个略微复杂一些.python源码到执行的中间会有一个步骤生成字节码,实际执行的其实是这个字节码.因此,如果想跳过第一步,我们可以对源码进行编译,编译为字节码后直接执行字节码." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## python的字节码\n", | ||
"\n", | ||
"cpython实际上运行的是字节码,我们所写的python源文件之所以可以被执行,其原因是当python程序加载时,源文件会被先翻译为字节码.然后保存在同级文件的`__pycache__`文件夹下.下次执行是,如果`__pycache__`下的字节码文件和源码文件修改时间匹配一致就会直接执行`__pycache__`中缓存的字节码,不一致则会将对应的字节码替换.\n", | ||
"\n", | ||
"\n", | ||
"python的字节码在旧版本中有两种,一种是常规的`.pyc`文件,其加载速度相对于之前的.py文件有所提高,而且还可以实现源码隐藏,以及一定程度上的反编译;另一种是经过优化的`.pyo`文件(相比于`.pyc`文件更小),也可以提高加载速度.\n", | ||
"\n", | ||
"而现在则统一都是以`.pyc`作为后缀,优化的等级则显式的放在编译后文件的命名上." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### 编译模块到`.pyc`\n", | ||
"\n", | ||
"编译到`.pyc`我们可以使用两个标准库提供的工具:\n", | ||
"\n", | ||
"+ `py_compile`用于编译单独的源码文件\n", | ||
"\n", | ||
"+ `compileall`用于将某个文件夹下的源码文件递归地进行编译\n", | ||
"\n", | ||
"#### **单文件编译**\n", | ||
"\n", | ||
"使用`py_compile`单独编译一个文件的话,可以使用命令行工具\n", | ||
"\n", | ||
"```shell\n", | ||
"python -m py_compile /path/to/需要生成.pyc的脚本.py\n", | ||
"```\n", | ||
"即可.\n", | ||
"\n", | ||
"也可以在python脚本下使用,调用`py_compile`:\n", | ||
"\n", | ||
"```python\n", | ||
"py_compile.compile(file[, cfile[, dfile[, doraise]]])\n", | ||
"```\n", | ||
"\n", | ||
"其中\n", | ||
"+ `file`,表示需要生成`.pyc`或`.pyo`文件的源码路径;\n", | ||
"+ `cfile`,表示需要生成`.pyc`或`.pyo`文件的目标文件路径.它默认是以`.pyc`为扩展名的形如`xxxx.cpython-36.pyc`这样形式的字符串.此外,当且仅当所使用的解释器允许编译成.pyo文件,才能以`.pyo`结尾,这个后文说.\n", | ||
"+ `dfile`,表示编译出错时,将报错信息中的名字`file`替换为`dfile`\n", | ||
"+ `doraise`,设置是否忽略异常.若为`True`,则抛出`PyCompileError`异常;否则直接将错误信息写入`sys.stderr`.\n", | ||
"\n", | ||
"\n", | ||
"类似的,编译整个文件夹可以使用命令行工具`compileall`\n", | ||
"\n", | ||
"```shell\n", | ||
"python -m compileall <dir>\n", | ||
"```\n", | ||
"\n", | ||
"常用的参数有`-b`表示写入它们源文件同级并且同名不同后缀\n", | ||
"\n", | ||
"也可以在python脚本下使用,调用`compileall`:\n", | ||
"\n", | ||
"```python\n", | ||
"import compileall\n", | ||
"\n", | ||
"compileall.compile_dir('Lib/', force=True)\n", | ||
"```\n", | ||
"compile_dir参数有:\n", | ||
"\n", | ||
"+ `dir`目标文件夹\n", | ||
"+ `maxlevels=10`文件夹最大递归深度\n", | ||
"+ `ddir=None`检查ddir中二进制文件的时间戳,如果与目标文件不一致才会编译\n", | ||
"+ `force=False`强制编译\n", | ||
"+ `rx=None`使用`re`查找目标文件夹下要编译的文件\n", | ||
"+ `quiet=0`是否输出编译过程\n", | ||
"+ `legacy=False`字节码文件是否被写入它们源文件同级并且同名(python2的默认编译行为),它们可能覆盖由另一版本的Python创建的字节码文件\n", | ||
"+ `optimize=-1`优化等级,\n", | ||
"+ `workers=1`多进程编译\n", | ||
"\n", | ||
"\n", | ||
"#### **编译优化**\n", | ||
"\n", | ||
"使用命令行工具编译是否优化的标志位在python一级,使用`-O`表示优化等级为1,`-OO`表示优化等级为2级(相比1级去掉了文档字符串),经过优化的字节码文件会被默认的命名为`xxxx.cpython-36.opt-2.pyc`这样的形式.\n", | ||
"\n", | ||
"单文件的1级优化编译\n", | ||
"\n", | ||
"```shell\n", | ||
"python -O -m py_compile /path/to/需要生成.pyo的脚本.py\n", | ||
"```\n", | ||
"\n", | ||
"文件夹下递归2级优化编译:\n", | ||
"\n", | ||
"```shell\n", | ||
"python -O -m compileall -b <path>\n", | ||
"```\n", | ||
"\n", | ||
"需要注意编译后的字节码文件并不会提高运行时的执行速度,只会提高模块的加载速度." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## 反编译\n", | ||
"\n", | ||
"指望通过编译python源文件来反编译是不现实的,使用`uncompyle6`工具可以非常轻易的将`.pyc`反编译为源码`.py`文件.`uncompyle6`可以直接使用`pip`工具安装." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## 结语\n", | ||
"\n", | ||
"python的编译功能看起来挺鸡肋的,但还是可以在一些特定环境下发挥用处的,比如:\n", | ||
"\n", | ||
"+ 硬盘空间资源紧张的硬件上,像树莓派,结合`zipapp`,可执行文件可以被控制在一个很小的程度.\n", | ||
"+ 吓唬外行,通常知道点python的也不会知道可以`编译`,编译后的字节码人类也是没法直接解读的,一定程度上可以防止源码泄漏\n" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.6.4" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 2 | ||
} |