(Python学习9)Python虚拟机中的一般表达式
1、准备工作
执行.py程序时,Python解释器对PyCodeObject的co_code存储的字节码进行解释执行,同时co_consts存储了常量,co_names存储了变量名称。用compile()可将.py编译为PyCodeObject,dis模块可对PyCodeObject的字节码反编译。
构建工具co_dist.py:
source = open('test.py').read() co = compile(source, 'test.py', 'exec') print("const\t:\t", co.co_consts) print("name\t:\t", co.co_names) import dis dis.dis(co)
测试程序test.py:
i = 1 s = "efei" d = {} l = []
执行co_dist可得结果:
const : (1, 'efei', None) name : ('i', 's', 'd', 'l') 1 0 LOAD_CONST 0 (1) 3 STORE_NAME 0 (i) 2 6 LOAD_CONST 1 ('efei') 9 STORE_NAME 1 (s) 3 12 BUILD_MAP 0 15 STORE_NAME 2 (d) 4 18 BUILD_LIST 0 21 STORE_NAME 3 (l) 24 LOAD_CONST 2 (None) 27 RETURN_VALUE
可看到consts存储的对象,names存储的值,并且都是以PyTupleObject存储的。
紧接着是字节码的反编译结果,从左只右依次表示:源代码中行号、co_code中字节序号、汇编指令、参数、以及参数代表的含义。
以分析框架为主,如无必要,对汇编指令对应的函数不再具体分析。
---------------------------------------------------------------------------------------------
2、简单对象创建
看第一条 i = 1:
1 0 LOAD_CONST 0 (1) 3 STORE_NAME 0 (i)
其中:
case LOAD_CONST: x = GETITEM(consts, oparg); Py_INCREF(x); PUSH(x); #define GETITEM(v, i) PyTuple_GetItem((v), (i))
oparg即为传入的参数0。所以LOAD_CONST 0 解释为:从consts中取出第0个对象x,将x压入运行栈中。
case STORE_NAME: w = GETITEM(names, oparg); v = POP(); if ((x = f->f_locals) != NULL) { if (PyDict_CheckExact(x)) err = PyDict_SetItem(x, w, v); else err = PyObject_SetItem(x, w, v); Py_DECREF(v); if (err == 0) continue; break; }
STORE_NAME 0 的解释为:从名为names中取出第0个对象w,运行时栈中弹出v,将(w, v)放入_dict对象f_locals中,即局部变量。
这两句汇编指令运行的状态图如下:
2 6 LOAD_CONST 1 ('efei') 9 STORE_NAME 1 (s)
运行时的状态图:
3 12 BUILD_MAP 0 15 STORE_NAME 2 (d) 4 18 BUILD_LIST 0 21 STORE_NAME 3 (l) 24 LOAD_CONST 2 (None) 27 RETURN_VALUE
类似,只不过BUILD_MAP创建一个_dict对象,BUILD_LIST创建一个_list对象。
case BUILD_MAP: x = _PyDict_NewPresized((Py_ssize_t)oparg); PUSH(x); case BUILD_LIST: x = PyList_New(oparg); if (x != NULL) { for (; --oparg >= 0;) { w = POP(); PyList_SET_ITEM(x, oparg, w); } PUSH(x);
BUILD_MAP 直接创建一个空对象,然后入栈;BUILD_LIST创建一个对象之后会将前面oparg栈中的对象弹出放入_list对象中,然后将_list对象入栈。
最后两条语句需注意,将返回值(Node)压入栈中,供ETURN_VALUE调用。
最终如下:
3、复杂内建对象创建
i = 1 s = "efei" d = {"1":1, "2":2} l = [1, 2]
const : (1, 'efei', '1', 2, '2', None) name : ('i', 's', 'd', 'l') 1 0 LOAD_CONST 0 (1) 3 STORE_NAME 0 (i) 2 6 LOAD_CONST 1 ('efei') 9 STORE_NAME 1 (s) 3 12 BUILD_MAP 2 // 新建空_dict,并压入栈 15 LOAD_CONST 0 (1) // 将第一个元素的key压入栈 18 LOAD_CONST 2 ('1') // 将第一个元素的val压入栈 21 STORE_MAP // 将第一个元素放入_dict 22 LOAD_CONST 3 (2) // 第二个元素…… 25 LOAD_CONST 4 ('2') 28 STORE_MAP 29 STORE_NAME 2 (d) // 将_dict放入f_locals 4 32 LOAD_CONST 0 (1) // 将list中的元素压入栈 35 LOAD_CONST 3 (2) // …… 38 BUILD_LIST 2 // 创建_list,并将栈中的前2个对象放入_list 41 STORE_NAME 3 (l) // 将_list放入f_locals 44 LOAD_CONST 5 (None) 47 RETURN_VALUE
如上,需注意 d = {"1":1, "2":2} 的汇编指令与《Python源码剖析》中不同,书中用的Python2.5,本处用的Python3.3,其实Python2.7.5也是上述指令。
case STORE_MAP: w = TOP(); /* key */ u = SECOND(); /* value */ v = THIRD(); /* dict */ STACKADJ(-2); assert (PyDict_CheckExact(v)); err = PyDict_SetItem(v, w, u); /* v[w] = u */ Py_DECREF(u); Py_DECREF(w);
比Pyhotn2.5逻辑更清晰,汇编指令更清晰了,STORE_MAP的操纵也更清晰了。
4、其他
a = 5 b = a c = a + b print(c)
const : (5, None) name : ('a', 'b', 'c', 'print') 1 0 LOAD_CONST 0 (5) 3 STORE_NAME 0 (a) 2 6 LOAD_NAME 0 (a) // 在名字空间中搜索a,并将其值压入栈 9 STORE_NAME 1 (b) 3 12 LOAD_NAME 0 (a) 15 LOAD_NAME 1 (b) 18 BINARY_ADD // 相加,将结果压入栈 19 STORE_NAME 2 (c) 4 22 LOAD_NAME 3 (print) // 搜索并压入函数对象 25 LOAD_NAME 2 (c) // 搜索并压入C对应的对象 28 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 31 POP_TOP 32 LOAD_CONST 1 (None) 35 RETURN_VALUE
b = a 的汇编指令为 LOAD_NAME 与 STORE_NAME。LOAD_NAME实质为,依次在f_locals、f_globals、f_builtins中搜索,如果找到则将相应的对象压入栈中,找不到会终止解释器运行。(其实栈中存储的均为对象的指针……)
执行后,已经将具体值赋给了b,图示如下:
BINARY_ADD 实质为处理对象相加的函数。需注意:若两对象都是PyStringObject或PyIntObject,处理速度较快,否则会调用其他函数处理速度较慢。
print的汇编指令也与书中不同,Python3.x中print已经变作一个函数,也是一种对象,可以将其指针压入栈中。可以推测,CALL_FUNCTION会通过传入的参数来分辨函数指针与该函数需用的参数。CALL_FUNCTION最终会将执行结果压入栈中,故调用结束时还需弹出。
为验证推测,添加语句print(a,b)汇编指令会添加
5 32 LOAD_NAME 3 (print) 35 LOAD_NAME 0 (a) 38 LOAD_NAME 1 (b) 41 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 44 POP_TOP
和预测相符。
相关文章
- python学习系列-2-函数
- 【无标题】python——详解collections工具库
- 【2022】Python自动化测试,软件测试最全学习路线......
- Python字符串,整型,浮点数相互转化
- 地球引擎初级教程——Python API 语法(内涵JavaScript转python工具包介绍)
- Google Earth Engine——官方python/JavaScript介绍内附学习链接
- python 知识架构拓扑图
- python 装饰器详解
- python+pip+adb
- Python环境搭建详解(Window平台)
- 【python学习】Day2打卡-20210426
- Python 音视频方面资源大全
- python学习笔记之module && package
- 《贝叶斯思维:统计建模的Python学习法》一2.4 Monty Hall难题
- 《贝叶斯思维:统计建模的Python学习法》一2.8 练习
- 《Python数据科学指南》——1.14 返回一个函数
- 【21天学习经典算法】插入排序(附Python完整代码)
- python学习之二叉树的实现详解
- 基于Python(pyqt)实现人工智能基础(强化学习求解迷宫问题)【100010442】
- Python关键字参数
- Python 界面tkinter教程大全之 如何解决 ModuleNotFoundError: No module named ‘_tkinter‘ on macOS
- python操作mysql数据库系列-操作MySql数据库(三)
- 高校教编程是否应该将Python作为主语言
- Python学习---ModelForm拾遗180325
- python学习(三)之list和tuple
- 使用 python 读写中文json
- 小学生python游戏编程arcade----碰撞精灵消失问题
- 数学之路-python计算实战(9)-机器视觉-图像插值仿射
- Python类的定义、方法和属性使用
- 【Python学习】——语言风格(变量赋值、深浅拷贝、for循环陷阱)
- 学习笔记(47):Python实战编程-pack布局
- 学习笔记(08):Python网络编程&并发编程-实现服务端可以对多个客户端提供服务