硬核!Python 四种变量的代码对象和反汇编分析
作者 | 大奎
整理 | 阳哥
来源丨Python数据之道
在Python基础的学习过程中,对变量和参数的理解有助于我们从更基础层面了解Python语言的运行。在这个过程中,还是有不少冷门和细节的地方需要进一步熟悉。今天我们来分享Python四种变量的代码对象和反汇编分析~
本文我们查看代码对象属性,可以清楚认识变量的分类。反汇编代码,可以弄清各变量在字节码处理上的不同。
代码对象
每个函数都有代码对象(code object): code,这个对象中存储了函数的字节码和编译信息。其中,以 co_ 开头的属性,是本文我们需要关注的。
以下代码中,我们建立了内置变量 builtins.b、全局变量 g、函数 outer 中新建了局部变量 o1、 o2,还有自由变量 e。内嵌函数 inner 新建局部变量 i1、 i2,引用外部变量 o1、 o2、 e,引用了外部全局变量 g 和内置变量 builtins.b。之后 outer 函数 和 inner 嵌入函数的代码对象:
import builtinsbuiltins.b = 'builtins'
g = 'global'vars = 'argcount cellvars consts filename firstlineno flags freevars lnotab name names nlocals stacksize varnames'.split()def outer(o1,o2='o2'):e = 'enclose'def inner(i1,i2='i2'):print(i1,i2,o1,o2,e,g,b)return inner fun = outer('o1')for var in vars:s = eval('outer.__code__.co_%s'%var)s2 = eval('fun.__code__.co_%s'%var) print('%15s:%s|%s'%(var,s,s2)) fun('i1')
可得输出结果。
argcount:2|2cellvars:('e', 'o1', 'o2')|()consts:(None, 'enclose', 'i2', <code object inner at 0x004F5D88, file "scope_of_variable.py", line 10>, 'outer.<locals>.inner', ('i2',))|(None,)filename:scope_of_variable.py|scope_of_variable.pyfirstlineno:8|10flags:3|19freevars:()|('e', 'o1', 'o2')lnotab:b'\x00\x01\x04\x01\x12\x02'|b'\x00\x01'name:outer|innernames:()|('print', 'g', 'b')nlocals:3|2stacksize:4|8varnames:('o1', 'o2', 'inner')|('i1', 'i2')
i1 i2 o1 o2 enclose global builtins
根据以下字段意义,可进一步确认内省函数中变量的分类和作用域。
co_argcount 函数形式参数个数
co_cellvars 被内部包含函数引用的变量组成的元组。也就是所谓 自由变量
co_freevars 当前函数 引用的外部自由变量 组成的元组,和上个 co_cellvars 是相对的概念
co_consts 常量,包括如下:
None 函数返回值,系统自带
从前往后数所有字面常量
内嵌函数代码对象 code object
内嵌函数的 qual_name 常量,如:outer..inner
co_names 本函数用到的 全局变量、系统内置变量
co_nlocals 本函数的局部变量个数
co_varnames 本函数 局部变量 ,不含被引用自由变量,包含形式参数和内嵌函数绑定变量
上文中的常量、局部变量、全局变量分别存储在不同的元组中,在代码编译为字节码时,使用不同指令来取得、修改。自由变量的处理则更为复杂一些。
使用反汇编可以一探其中奥秘。
反汇编代码
编程语言分为编译型和解释型。编译型如 C 语言,预先 gcc 成.exe 文件。解释型如 PHP ,在执行时才一条条顺序读入,顺序解释执行。Python 则采用编译成中间代码,再虚拟机运行的方式。编译的中间代码称为字节码,虚拟机每个平台是不同的,虚拟机负责将字节码转义为对应平台的二进制指令。这点类似于 Java 的字节码,所以两者都具有平台无关性:一处编码,处处运行。原理就在这里。
Python 中的 dis 模块,可以把程序反汇编为字节码。Python 字节码使用 LOAD_FAST 0
这种形式,前面是指令 LOAD_FAST,后面是参数 0。整条语句就是:”把局部变量元组 0 处变量,加载到当前栈顶。“
这里的局部变量元组是上文的 co_varnames。
这里的当前栈顶表示的是当前帧的计算栈。CPython 虚拟机是基于帧栈的机器,也就是说 CPython 虚拟机运行在一个栈里。帧栈里是每次函数调用形成的帧,而每个帧里面是计算栈和块栈。计算栈是函数主体运行的部分,块栈是程序中 with、 try、循环执行的地方。
使用如下代码,来反汇编 outer 和 inner 函数。
import dis
print(dis.dis(outer()))
这是反汇编 outer()
执行结果,也就是 inner
函数本身。
可得结果如下。首先加载全局变量元组第 0 个元素 print 函数。然后加载局部变量 i1、 i2,之后加载自由变量 o1、 o2、 e,再加载全局变量 e 和 g,调用函数 print 输出结果。结果弹栈,加载 None,返回结果。
12 0 LOAD_GLOBAL 0 (print)2 LOAD_FAST 0 (i1)4 LOAD_FAST 1 (i2)6 LOAD_DEREF 1 (o1)8 LOAD_DEREF 2 (o2)10 LOAD_DEREF 0 (e)12 LOAD_GLOBAL 1 (g)14 LOAD_GLOBAL 2 (b)16 CALL_FUNCTION 718 POP_TOP20 LOAD_CONST 0 (None)22 RETURN_VALUE
以及print(dis.dis(outer))
可得结果。
9 0 LOAD_CONST 1 ('enclose')2 STORE_DEREF 0 (e)11 4 LOAD_CONST 5 (('i2',))6 LOAD_CLOSURE 0 (e)8 LOAD_CLOSURE 1 (o1)10 LOAD_CLOSURE 2 (o2)12 BUILD_TUPLE 314 LOAD_CONST 3 (<code object inner at 0x00225D88, file "scope_of_variable_bytecode.py", line 11>)16 LOAD_CONST 4 ('outer.<locals>.inner')18 MAKE_FUNCTION 9 (defaults, closure)20 STORE_FAST 2 (inner)13 22 LOAD_FAST 2 (inner)24 RETURN_VALUEDisassembly of <code object inner at 0x00225D88, file "scope_of_variable_bytecode.py", line 11>:12 0 LOAD_GLOBAL 0 (print)2 LOAD_FAST 0 (i1)4 LOAD_FAST 1 (i2)6 LOAD_DEREF 1 (o1)8 LOAD_DEREF 2 (o2)10 LOAD_DEREF 0 (e)12 LOAD_GLOBAL 1 (g)14 LOAD_GLOBAL 2 (b)16 CALL_FUNCTION 718 POP_TOP20 LOAD_CONST 0 (None)22 RETURN_VALUE
None
以上代码中。- 第 9 行,加载常量并且存储为自由变量。- 第 11 行,加载常量作默认值、加载自由变量构成元组,以构成闭包参数,然后编译内部函数 inner 字节码,使用默认值和闭包做参数构建内部函数 inner。- 第 13 行,返回函数 inner。
这也是闭包的原理,闭包中的自由变量 i1、 i2、 e 和函数 inner 构成了一个整体代码块,自由变量附着在 inner 函数上,形成了一个特殊作用域。
以上所有指令,都定义在 include/opcode.h 中,它指代的整数在 Python 程序的主循环 ceval.c 中分别判断,然后拆解成 C 语言指令执行。可以参考 CPython 的 ceval 源代码
https://github.com/python/cpython/blob/b11a951f16f0603d98de24fee5c023df83ea552c/Python/ceval.c
总结
Python 中的局部、全局、内置、自由变量四种类型变量在作用域和使用上各不相同,使用代码对象,反汇编可以更清楚认识变量情况,为理解闭包、装饰器打下基础。
往
期
回
顾
技术
用Python写一个天天酷跑
资讯
Nginx宣布在俄罗斯禁止贡献
技术
学会用Opencv做贪吃蛇游戏
技术
一行Python代码能干嘛?来看!
分享
点收藏
点点赞
点在看
硬核!Python 四种变量的代码对象和反汇编分析相关推荐
- python中long类型_浅谈python 四种数值类型(int,long,float,complex)
Python支持四种不同的数值类型,包括int(整数)long(长整数)float(浮点实际值)complex (复数),本文章向码农介绍python 四种数值类型,需要的朋友可以参考一下. 数字数据 ...
- python数字类型floatcomplexint_浅谈python 四种数值类型(int,long,float,complex)
Python支持四种不同的数值类型,包括int(整数)long(长整数)float(浮点实际值)complex (复数),本文章向码农介绍python 四种数值类型,需要的朋友可以参考一下. 数字数据 ...
- python 期权量化交易_两种欧式期权的四种支付方式以代码实现
不说废话了,直接上干货. 出两个问题,大家检验一下自己是否是"伪金融人". 顺便也说一个金融趣闻. 一.检验你是否是"金融人"的第一个考验 你能一眼看出来下图是 ...
- python 释放链表节点_四种常见链表的实现及时间复杂度分析(Python3版)
四种常见的链表包括:单向链表,单向循环链表,双向链表,双向循环链表. 要实现的链表操作包括 - is_empty() 判断链表是否为空 - length() 求链表长度 - traversing() ...
- python 四种逐行读取文件内容的方法
目录 方法一:readline函数 方法二:一次读取多行数据 方法三:直接for循环 方法四:使用fileinput模块 下面是四种Python逐行读取文件内容的方法, 并分析了各种方法的优缺点及应用 ...
- Python四种形式模块的形式与调用
一.什么是模块? 模块是一系列功能的集合体,而函数是某一个功能的集合体,因此模块可以看成是一堆函数的集合体.一个py文件内部就可以放一堆函数,因此一个py文件就可以看成一个模块.如果这个py文件的文件 ...
- FreeMarker四种变量的用法
原文:http://www.656463.com/article/286 摘要: freemarker的变量可以分为四种,分别是数据模型的变量[root中的变量],模板中的变量使用[<#assi ...
- python四种可变类型_SICP Python 描述 2.4 可变数据
2.4 可变数据 我们已经看到了抽象在帮助我们应对大型系统的复杂性时如何至关重要.有效的程序整合也需要一些组织原则,指导我们构思程序的概要设计.特别地,我们需要一些策略来帮助我们构建大型系统,使之模块 ...
- python 实数如何取整_从面试官角度提问:15道硬核Python面试题,论面霸是如何炼成的...
见过面试题也不少了,总之了一句话:面试前备好功课,面试中临危不乱,面试后谦虚有礼!这只是我本人总结的一些面试三要素,需要的可以参考参考,话不多了,今天为大家找了比较硬核的15道面试题,希望能够对各位有 ...
最新文章
- Tech UP——EGO北京分会成立啦
- 排序算法 | 直接插入排序算法的图解、实现、复杂度和稳定性分析
- python怎么输入一个数字并调用_python如何直接输入上一句话,如何快速打出上一句话...
- BZOJ5251:[九省联考2018]劈配——题解
- Team Foundation Server简介
- 使用ajax获取后台数据怎么打印,我用ajax获取后台数据并展示在前端页面的方法【源码】...
- pyecharts绘制地铁图_安利一个绘制地铁线路KMZ的利器 号称国产谷歌地球
- 参考文献标号字体_论文参考文献字体大小
- java外卖系统源码_java外卖订餐系统小项目
- apache+php+mysql 常见集成环境安装包
- Vue 中英文切换设置
- kafka 修改分区_kafka分区
- Latex/CTeX WinEdt7.0 连续查找替换功能 “如何统计字数”
- 微博html5到桌面,HTML 分享页面到QQ/微信、微博等平台
- 星起航:跨境电商行业卖家可利用新技术打造成熟供应链
- 星瀚资本杨歌,慧聪集团姚永超,腾飞资本任溶 | TO B创业过冬策略,开源节流...
- chrome android 中文版下载,谷歌chrome安卓版中文
- 从Amazon与阿里巴巴看物联网商机
- Tomcat9最大并发连接数的修改方法
- 区块链技术,让数字政务跑出“加速度”