python3.6 源码分析(二):另一个例子
来源:互联网 发布:origin淘宝买游戏 编辑:程序博客网 时间:2024/06/05 20:45
还是从字节码开始分析
a = 1b = 2c = a + b
编译:
0 LOAD_CONST 0 (1)2 STORE_NAME 0 (a)4 LOAD_CONST 1 (2)6 STORE_NAME 1 (b)8 LOAD_NAME 0 (a)10 LOAD_NAME 1 (b)12 BINARY_ADD14 STORE_NAME 2 (c)
前面两个变量绑定上节已经分析过了,我们从第八个字节开始分析,by the way,python3.6开始字节码变成了固定两个字节。
首先是LOAD_NAME这个字节码,顾名思义,应该是将名字对应的值加载到栈顶,让我们看看是不是这样:
TARGET(LOAD_NAME) { PyObject *name = GETITEM(names, oparg); PyObject *locals = f->f_locals; PyObject *v; if (locals == NULL) { PyErr_Format(PyExc_SystemError, "no locals when loading %R", name); goto error; } if (PyDict_CheckExact(locals)) { v = PyDict_GetItem(locals, name); Py_XINCREF(v); } else { v = PyObject_GetItem(locals, name); if (v == NULL) { if (!PyErr_ExceptionMatches(PyExc_KeyError)) goto error; PyErr_Clear(); } } if (v == NULL) { v = PyDict_GetItem(f->f_globals, name); Py_XINCREF(v); if (v == NULL) { if (PyDict_CheckExact(f->f_builtins)) { v = PyDict_GetItem(f->f_builtins, name); if (v == NULL) { format_exc_check_arg( PyExc_NameError, NAME_ERROR_MSG, name); goto error; } Py_INCREF(v); } else { v = PyObject_GetItem(f->f_builtins, name); if (v == NULL) { if (PyErr_ExceptionMatches(PyExc_KeyError)) format_exc_check_arg( PyExc_NameError, NAME_ERROR_MSG, name); goto error; } } } } PUSH(v); DISPATCH(); }
足足50行,加载个名字而已,为啥这么麻烦。
第一行GETITEM ,看看宏定义:
#define GETITEM(v, i) PyTuple_GetItem((v), (i))
原来names域是一个tuple,好吧,第一步貌似就已经将名字取到了,接下来还要做啥?当然是取名字对应的值了。
后面的事情简直不能更简单,注意这3行:
v = PyDict_GetItem(locals, name);v = PyDict_GetItem(f->f_globals, name);v = PyDict_GetItem(f->f_builtins, name);
这就是python寻找变量的值的顺序了,依次分别尝试从locals,globals,builtins里面去寻找这个名字对应的值,最后:
PUSH(v);
将找到的值压入栈顶,完了。
至于那些个
Py_INCREF(v);
这些东西,是增加垃圾回收引用计数,对我们的分析没有影响,就不管他了。
还有一个问题:最后一行那个DISPATHC()又是啥?
点进去一看,哈哈,就是个continue。
下一步
接着两个LOAD_NAME的是一个BINARY_ADD,想都不用想,一定是把两个栈顶的值POP出来加起来,然后将结果放回栈顶,让我们验证一下:
TARGET(BINARY_ADD) { PyObject *right = POP(); PyObject *left = TOP(); PyObject *sum; if (PyUnicode_CheckExact(left) && PyUnicode_CheckExact(right)) { sum = unicode_concatenate(left, right, f, next_instr); /* unicode_concatenate consumed the ref to left */ } else { sum = PyNumber_Add(left, right); Py_DECREF(left); } Py_DECREF(right); SET_TOP(sum); if (sum == NULL) goto error; DISPATCH(); }
可以说代码是非常的短了,看看做了些什么:
PyObject *right = POP(); PyObject *left = TOP(); PyObject *sum;
首先是取出两个加数,然后定义了和的指针,注意left是TOP取出来的,说明此时left还在栈顶,然后发生了一些奇怪的事情:
if (PyUnicode_CheckExact(left) && PyUnicode_CheckExact(right)) { sum = unicode_concatenate(left, right, f, next_instr); }
居然可以做字符串加法。。还好我们传入的都是数字,所以应该到了else里面:
else { sum = PyNumber_Add(left, right); Py_DECREF(left); }Py_DECREF(right); SET_TOP(sum);
结果为sum,SET_TOP这个宏直接将栈顶元素指向了sum,此时栈顶就是计算结果了。
还差最后一步,将结果赋值给c,STORE_NAME,我们已经不能更熟悉。。略。
总结一下
从最初的轻视,到发现一个LOAD_NAME就有50行代码的惊讶,再到最后的释然,这就是源码分析时的快感,让源码调动起自己的情绪,随着代码去旅行,可能就是最好的分析方法。
- python3.6 源码分析(二):另一个例子
- python3.6 源码分析(一)
- RXAndroid源码分析(二) 简单的例子
- python3.6 源码分析(四):函数调用
- python3.6 源码分析(三):创建函数
- python3.6 源码分析(五):类的创建
- DX90SDK SDK源码分析(二) 推模式的例子
- DX90SDK SDK源码分析(二) 推模式的例子
- python3.6 爬虫例子
- JUnit源码分析(二)
- Log4net源码分析(二)
- Log4net源码分析(二)
- Logcat源码分析(二)
- FFMPEG源码分析(二)
- FFMPEG源码分析(二)
- opendpi 源码分析(二)
- Log4net源码分析(二)
- pomelo源码分析(二)
- scp与cp复制目录问题
- 4.9 chmod和fchmod函数
- 字符串中的空格处理
- Django 中针对基于类的视图添加 csrf_exempt
- 最强 Android Studio 使用小技巧和快捷键
- python3.6 源码分析(二):另一个例子
- CUDA4.0 inline PTX汇编程序开发
- win7+hadoop2.7.2+maven+idea
- Linux下双网卡双网关配置路由表
- PHP中的 抽象类(abstract class)和 接口(interface)
- 基于Hive+sparkSQL的人力资源系统实例
- NOIP 2008 火柴棒等式
- Java或APK包名正则表达式
- Java 中执行CMD命令(待续)