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学习9)Python虚拟机中的一般表达式的更多相关文章

  1. Python虚拟机中的一般表达式(三)

    其他一般表达式 在前两章:Python虚拟机中的一般表达式(一).Python虚拟机中的一般表达式(二)中,我们介绍了Python虚拟机是怎样执行创建一个整数值对象.字符串对象.字典对象和列表对象.现 ...

  2. Python虚拟机中的一般表达式(二)

    复杂内建对象的创建 在上一章Python虚拟机中的一般表达式(一)中,我们看到了Python是如何创建一个空的字典对象和列表对象,那么如果创建一个非空的字典对象和列表对象,Python的行为又是如何呢 ...

  3. Python学习day09 - Python进阶(3)

    figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...

  4. Python学习day05 - Python基础(3) 格式化输出和基本运算符

    figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...

  5. Python学习教程:Pandas中第二好用的函数

    从网上看到一篇好的文章是关于如何学习python数据分析的迫不及待想要分享给大家,大家也可以点链接看原博客.希望对大家的学习有帮助. 本次的Python学习教程是关于Python数据分析实战基础相关内 ...

  6. [持续更新] Python学习、使用过程中遇见的非代码层面知识(想不到更好的标题了 T_T)

    写在前面: 这篇博文记录的不是python代码.数据结构.算法相关的内容,而是在学习.使用过程中遇见的一些没有技术含量,但有时很令人抓耳挠腮的小东西.比如:python内置库怎么看.python搜索模 ...

  7. python学习笔记013——模块中的私有属性

    1 私有属性的使用方式 在python中,没有类似private之类的关键字来声明私有方法或属性.若要声明其私有属性,语法规则为: 属性前加双下划线,属性后不加(双)下划线,如将属性name私有化,则 ...

  8. Python学习笔记—Python基础1 介绍、发展史、安装、基本语法

    第一周学习笔记: 一.Python介绍      1.Python的创始人为吉多·范罗苏姆.1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言 ...

  9. python学习笔记-python程序运行

    小白初学python,写下自己的一些想法.大神请忽略. 安装python编辑器,并配置环境(见http://www.cnblogs.com/lynn-li/p/5885001.html中 python ...

随机推荐

  1. MVC页面声命周期

    MVC页面声命周期 ASP.Net请求处理机制初步探索之旅 - Part 4 WebForm页面生命周期   开篇:上一篇我们了解了所谓的请求处理管道,在众多的事件中微软开放了19个重要的事件给我们, ...

  2. oracle 创建用户,授权用户,创建表,查询表

    原文:oracle 创建用户,授权用户,创建表,查询表 oracle 创建用户,授权用户,创建表,查询表 假设oracle10g所有的都已经安装和配置好 第一步:win+R,进入运行,cmd; 第二步 ...

  3. Yii中CDbCriteria常用总结

    Yii的Active Recorder包装了很多. 特别是把SQL中 把where,order,limit,IN/not IN,like等常用短句都包含进CDbCriteria这个类中去,这样整个代码 ...

  4. APP-随身听

    简单到复杂听你的专属音响界,听金融.听物业,听新闻和其他节目专辑,简要介绍了新的音频应用,给你不一样的聆听体验.还记得老歌做?这里有.您留声机的一部分!很简单的音频应用,随时随地与此应用程序来听你的私 ...

  5. webkit内核下的mouseup后mousemove自动触发问题及解决方法

    如题,就以chrome为代表举例说明遇到mousemove的问题. 为body分别绑定onmousedown.onmousemove.onmouseup,并为触发时打印至控制台.代码如下(同学不要忘记 ...

  6. SQL点滴27—性能分析之执行计划

    原文:SQL点滴27-性能分析之执行计划 一直想找一些关于SQL语句性能调试的权威参考,但是有参考未必就能够做好调试的工作.我深信实践中得到的经验是最珍贵的,书本知识只是一个引导.本篇来源于<I ...

  7. 增加 Java 有几个好习惯表现

    以下是一些参考网络资源中的摘要Java编程在一些地方尽可能做. 1. 尝试使用单个例如在合适的场合 使用单例可以减轻负荷的负担,缩短加载时间.提高装载效率,但并不是所有的地方都适合一个案例.简单的说, ...

  8. Ubuntu中改变文件的默认打开方式

    其实最简单的方法是右键,在属性中修改,不过这样做没啥意义. ubuntu中与文件的打开方式相关的配置文件有四个: /etc/gnome/defaults.list 这是全局配置文件 /usr/shar ...

  9. solr中重跑索引

    solr与.net系列课程(八)solr中重跑索引的注意事项   solr与.net系列课程(八)solr中重跑索引的注意事项 我们如果在项目中使用solr,那肯定就是把数据库中的数据跑进solr服务 ...

  10. C#实现文档转换成PDF

    网上有很多将doc.ppt.xls等类型的文档转换成pdf的方法,目前了解到的有两大类: 1.使用虚拟打印机将doc.ppt.xls等类型的文档 2.使用OFFICE COM组件 我采用了第二种方法实 ...