Loading... # 0x01 <div class="tip inlineBlock info"> 本篇分析基于 **Python 3.7.9** 注意信息时效 </div> `print("Hello world!")`几乎是所有人学 Python 时做的第一件事,但是这段代码具体是怎么执行的呢?应该很多人就哽住了,那么我们就来从 **词法分析 —— 对象创建 —— 内建函数(builtin)执行 —— 内存管理 —— 编译成字节码 —— 虚拟机执行字节码** 卷一卷这个问题~ :joy: 注: 我们此处讨论的是使用 console 执行 `python hello.py`,`hello.py` 中的代码为 `print("Hello world!")`, 不讨论使用交互窗口 (REPL) 执行的模式。 # 词法分析 首先我们先写好我们的样例程序并命名为 `hello.py` ```python print("Hello world!") ``` 然后打开 REPL 使用 [AST](https://docs.python.org/zh-cn/3.7/library/ast.html) 生成该代码文件的抽象语法树,模拟生成 AST 的过程 ```python >>> import ast >>> text = open('hello.py').read() >>> text 'print("Hello World!")' >>> res = ast.parse(text, 'hello.py', 'exec') >>> res <_ast.Module object at 0x0000022E2DF7B808> >>> ast.dump(res) "Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Str(s='Hello World!')], keywords=[]))])" ``` 我们调整下这棵 AST 的结构 ```python Module( body=[ Expr( value=Call( func=Name( id='print', ctx=Load() ), args=[ Str(s='Hello World!') ], keywords=[] ) ) ] ) ``` 从这个结构我们可以看出我们的这段代码调用了一个 `print` 函数和创建了一个字符串 `Hello World!` # 对象创建 <div class="tip inlineBlock info"> 其实这一步是代码逻辑上的行为,为了方便阅读写在这里,并不是在此处执行的 </div> 我们从刚才的 AST 得知,程序创建了一个 `Hello World!` 字符串。因为 `Hello World!` 里所有字符都是 ASCII ,所以此处使用 C 头文件里的 `PyASCIIObject` 结构体创建了一个字符串对象。 ```cpp /* ASCII-only strings created through PyUnicode_New use the PyASCIIObject structure. state.ascii and state.compact are set, and the data immediately follow the structure. utf8_length and wstr_length can be found in the length field; the utf8 pointer is equal to the data pointer. */ typedef struct { PyObject_HEAD Py_ssize_t length; /* Number of code points in the string */ Py_hash_t hash; /* Hash value; -1 if not set */ struct { unsigned int interned:2; unsigned int kind:3; unsigned int compact:1; unsigned int ascii:1; unsigned int ready:1; unsigned int :24; } state; wchar_t *wstr; /* wchar_t representation (null-terminated) */ } PyASCIIObject; ```  创建完该字符串对象,然后将其作为函数参数传递给 `print` # 内建函数(builtin)执行 <div class="tip inlineBlock info"> 其实这一步是代码逻辑上的行为,为了方便阅读写在这里,并不是在此处执行的 </div> 函数调用涉及到一个命名空间查找的过程。我们此处调用的是 `print()` 函数,在当前命名空间下没有找到该函数的定义,接着在内建命名空间里找到了 `print()` 函数的定义。 > 补充一下知识点,Python 命名空间有如下几种: > > - 内建命名空间 > - 全局命名空间 > - 闭包命名空间 > - 局部命名空间 > > 且这些命名空间与 **作用域** 对应,Python 在运行时借助 dict 对象保存作用域中的名字,构成动态的 **命名空间** # 内存管理 <div class="tip inlineBlock info"> 其实这一步是代码逻辑上的行为,为了方便阅读写在这里,并不是在此处执行的 </div> 创建字符串对象需要内存空间,当我们创建这个字符串对象并以参数的形式把他的**引用**传递给 `print()` 函数时,`Hello World!` 字符串对象的引用计数 `ob_refcnt` 加一。当 `print()` 函数执行完成时,`Hello World!` 字符串对象的引用计数减一变成了 0,于是将对象回收。 ```cpp #define Py_DECREF(op) \ do { \ PyObject *_py_decref_tmp = (PyObject *)(op); \ if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \ 引用计数减一 \ --(_py_decref_tmp)->ob_refcnt != 0) \ _Py_CHECK_REFCNT(_py_decref_tmp) \ else \ 回收对象 \ _Py_Dealloc(_py_decref_tmp); \ } while (0) ``` # 编译成字节码并执行 > 这里插一个知识点,很多人都说 Python 是一个解释型语言,不需要进行编译。其实这句话不完全正确,Python 的确是没有把代码编译成机器码的过程,但是他会把代码编译成 **字节码**,然后使用 **虚拟机**逐行执行字节码。 > > 这和 Java 基本上是一致的,只不过 Python 将编译执行的过程全部揉在了解释器里自动执行,而不像 Java 需要先用编译器执行 javac 命令把 .java 代码文件编译成字节码 .class 文件,然后通过虚拟机 java 命令来执行字节码 .class 文件 我们模拟编译器编译一下上面这段代码,得到了一个 `code` 对象。 ```python >>> res = compile(text, 'hello.py', 'exec') >>> res <code object <module> at 0x0000022E2DF5CAE0, file "hello.py", line 1> ``` 这里还看不出啥来,我们继续用 `dis` 反编译一下生成的字节码 ```shell >>> import dis >>> dis.dis(res) 1 0 LOAD_NAME 0 (print) 2 LOAD_CONST 0 ('Hello World!') 4 CALL_FUNCTION 1 6 POP_TOP 8 LOAD_CONST 1 (None) 10 RETURN_VALUE ``` 其中,第一列是字节码 **偏移量** ,第二列是 **指令** ,第三列是 **操作数** (是不是特别像汇编~ :joy:)。我们来当一回人肉编译器(虚拟机)简单阅读下。 1. 加载名字 print 到栈顶 2. 加载常量 'Hello World!' 到栈顶 3. 调用函数 4. 删除栈堆顶部 5. 加载常量 None 到栈顶 6. 返回 None Python 虚拟机执行也跟我们阅读的差不多,整个执行流程也就到此为止了。 # Reference [AST 模块:用 Python 修改 Python 代码](https://pycoders-weekly-chinese.readthedocs.io/en/latest/issue3/static-modification-of-python-with-python-the-ast-module.html) [Python Developer's Guide Python - Design of CPython's Compiler CPython](https://devguide.python.org/compiler/) [dis --- Python 字节码反汇编器](https://docs.python.org/zh-cn/3/library/dis.html) [ast --- 抽象语法树](https://docs.python.org/zh-cn/3/library/ast.html?highlight=ast) 最后修改:2021 年 07 月 09 日 01 : 22 PM © 允许规范转载 赞赏 如果觉得我的文章对你有用,请随意赞赏 赞赏作者 支付宝微信