硬核!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--数据存储:pickle模块的使用讲解
在机器学习中,我们常常需要把训练好的模型存储起来,这样在进行决策时直接将模型读出,而不需要重新训练模型,这样就大大节约了时间。Python提供的pickle模块就很好地解决了这个问题,它可以序列化对象并保存到磁盘中&…

Linux虚拟内存和物理内存精华【美】
原文地址: 《Playing with Virtual Memory》 http://www.snailinaturtleneck.com/blog/2011/08/30/playing-with-virtual-memory/ 扩展阅读: 《Understanding Memory》 http://www.ualberta.ca/CNS/RESEARCH/LinuxClusters/mem.html 《Understanding Vir…

留不住客户?该从你的系统上找找原因了
本篇文章暨 CSDN《中国 101 计划》系列数字化转型场景之一。 《中国 101 计划——探索企业数字化发展新生态》为 CSDN 联合《新程序员》、GitCode.net 开源代码仓共同策划推出的系列活动,寻访一百零一个数字化转型场景,聚合呈现并开通评选通道࿰…

系统配置文件备份比较
客户的系统出各种问题,这次出了问题整整一天都没找出原因,都红脸了,最后发现是系统配置文件被改掉了,简直不能忍,所以写了这个脚本,放到定时任务里面,每天备份比较配置文件import difflib impor…

RPC是什么?为什么要学习RPC?
随着近几年分布式、微服务架构的火热,RPC在开发工作中使用的越来越多,也变的越来越重要。 今天我们来看RPC是什么,为什么要了解RPC,通过学习RPC我们能掌握什么内容? 什么是「RPC」 RPC 全称 Remote Procedure Call, wikipedia的部…

Lua学习笔记6:C++和Lua的相互调用
曾经一直用C写代码。话说近期刚换工作。项目组中的是cocos2dx-lua,各种被虐的非常慘啊有木有。新建cocos2dx-lua项目。打开class能够发现,事实上就是C项目啦,只是为什么仅仅有一类Appdelegate类呢?哈哈,我相信聪明的你一定猜到了&…

Redis消息通知系统的实现
Redis消息通知系统的实现Posted on 2012-02-29by 老王 http://huoding.com/2012/02/29/146最近忙着用Redis实现一个消息通知系统,今天大概总结了一下技术细节,其中演示代码如果没有特殊说明,使用的都是PhpRedis扩展来实现的。内存比如要推送一…

用 Python 实现答题卡识别!
作者 | 棒子胡豆来源丨CSDN博客答题卡素材图片:思路读入图片,做一些预处理工作。进行轮廓检测,然后找到该图片最大的轮廓,就是答题卡部分。进行透视变换,以去除除答题卡外的多余部分,并且可以对答题卡进行校…
Confluence 6 计划任务
管理员控制台能够允许你对 Confluence 运行的计划任务进行计划的调整,这些计划任务将会按照你的调整按时执行。可以按照计划执行的任务如下: Confluence 站点备份存储优化任务,清理 Confluence 的临时目录中的文件和缓存索引优化任务…

PHP共享内存段
在asp.net和java中都有共享内存,php除了可以使用Memcached等方式变通以外其实php也是支持共享内存的!需要安装扩展shmop 找到php安装源文件目录# cd /usr/local/php-5.4.0/ext/shmop # /usr/local/php/bin/phpize # ./configure --with-php-config/usr/l…

马尔科夫随机场的基本概念
1、随机过程: 描写叙述某个空间上粒子的随机运动过程的一种方法。它是一连串随机事件动态关系的定量描写叙述。随机过程与其他数学分支,如微分方程、复变函数等有密切联系。是自然科学、project科学及社会科学等领域研究随机现象的重要工具。 2、马尔科夫…

从事了两年 AI 研究,我学到了什么?
作者 | Tom Silver译者 | 弯月出品 | CSDN我从事人工智能研究的工作已经有两年了,有朋友问我都学到了什么,所以我想借本文分享一些迄今为止积累的经验教训。我将在本文中分享一些常见的经历,还会讨论相对具体的人工智能行业技巧。希望对大家能…

Windows server 2008普通用户不能远程登录问题
1、查登录权限 如果文件服务器没有为用户授权,那么用户自然就不能远程登录服务器系统了,为此笔者决定先仔细检查一下文件服务器系统是否为自己使用的登录账号,授予了远程登录权限。在进行这种检查时,笔者先是在文件服务器本地以系…

面向小白的最全 Python 可视化教程,超全的!
作者 | 俊欣来源丨关于数据分析与可视化今天小编总结归纳了若干个常用的可视化图表,并且通过调用plotly、matplotlib、altair、bokeh和seaborn等模块来分别绘制这些常用的可视化图表,最后无论是绘制可视化的代码,还是会指出来的结果都会通过调…

Atitit.文件搜索工具 attilax 总结
Atitit.文件搜索工具 attilax 总结 1. 指定目录按照体积大小精确搜索1 1.1. File Seeker 4.5 版本的可以,3.5版本的不行。。1 2. 按照文件内容搜索1 2.1. File Seeker2 2.2. Notepad2 2.3. FileLocator Pro 是一款专业的文件搜索软件,2 2.4. 百度硬盘搜索…

ulimit -SHn 65535 含义
linux下用ulimit设置连接数最大值,默认是1024.在高负载下要设置为更高,但最高只能为65535. ulimit只能做临时修改,重启后失效。可以加入ulimit -SHn 65535到 /etc/rc.local 每次启动启用。终极解除 Linux 系统的最大进程数和最大文件打开数限…

5.7-基于Binlog+Position的复制搭建
基本环境 MasterSlaveMySQL版本MySQL-5.7.16-X86_64MySQL-5.7.16-X86_64IP192.168.56.156192.168.56.157Port33063306数据库环境的部署:两边安装好相同的数据库软件,初始化,可以启动起来。检查事项:两边防火墙是否开启,…

PHP消息队列httpsqs安装与使用无错版
项目网址:http://code.google.com/p/httpsqs/使用文档:http://blog.s135.com/httpsqs/ 说明:由于需要安装的东西有些多,原文可能写的有些简略,所以适当补充了1.安装libevent-2.0.12-stable.tar.gzwget http://httpsqs.…

Windows Server 2012 系统群集
Windows Server 2012 系统群集本次将测试Windows Server 2012 系统群集功能。实验环境:4台服务器都为Windows Server 2012 DataCenter操作系统SRV2012服务器安装iSCSI目标服务器角色并配置2块虚拟磁盘给两台群集服务器共享使用。群集服务器安装群集功能,…

Python 中最强大的错误重试库
作者 | 费弗里来源丨Python大数据分析1 简介我们在编写程序尤其是与网络请求相关的程序,如调用web接口、运行网络爬虫等任务时,经常会遇到一些偶然发生的请求失败的状况,这种时候如果我们仅仅简单的捕捉错误然后跳过对应任务,肯定…

Spring Cloud云架构 - SSO单点登录之OAuth2.0登录流程(2)
上一篇是站在巨人的肩膀上去研究OAuth2.0,也是为了快速帮助大家认识OAuth2.0,闲话少说,我根据框架中OAuth2.0的使用总结,画了一个简单的流程图(根据用户名密码实现OAuth2.0的登录认证): 上面的图…

php的POSIX 函数以及进程测试
参考:http://cn.php.net/manual/zh/ref.posix.php <?phpecho posix_getpid(); //8805sleep(10);?>再用 #ps -ax这个时候如果多开开个浏览器请求,就会发现Apache自动增加了几个新的进程我们发现并非一直请求同一个进程重启apache # /usr/local/ap…

CAS (10) —— JBoss EAP 6.4下部署CAS时出现错误exception.message=Error decoding flow execution的解决办法...
CAS (10) —— JBoss EAP 6.4下部署CAS时出现错误exception.messageError decoding flow execution的解决办法 jboss版本: jboss-eap-6.4-CVE-2015-7501 jdk版本: 1.7.0_79 cas版本: cas 4.1.3 参考来源: Nabble: exception.messageErrordecodingflowexecution Nabble: Caused …

昇思MindSpore1.6发布 AI开发者体验再升级
智能时代,AI技术正在发挥至关重要的作用,而开源的深度学习框架不仅能够降低AI开发者的门槛,而且能够极大节省成本与时间,成为创新的重要推手。2022年3月26-27日,昇思MindSpore TechDay活动线上成功举办,包含…

深入解析:TRUNCATE TABLE 的内部原理解析与恢复思路
摘要 众所周知,truncate table 是一种快速清空表内数据的一种方式,与 delete 方式不同,truncate 只产生非常少的 redo 和 undo,就实现了清空表数据并降低表 HWM 的功能。本文主要围绕 truncate table 的实现原理和 truncate table…

Linux exec与重定向
exec和source都属于bash内部命令(builtins commands),在bash下输入man exec或man source可以查看所有的内部命令信息。 bash shell的命令分为两类:外部命令和内部命令。外部命令是通过系统调用或独立的程序实现的,如se…

俄罗斯 Android 系统受限,或将转用 HarmonyOS!
整理 | 郑丽媛出品 | CSDN近一个月来,受当前局势影响,部分底层工具、基础软件、开源项目已相继宣布在俄罗斯停服,期间不少人因此担忧:同出自美国且占据极大智能手机市场的 Android 和 iOS 是否会趁机“作乱”?结果&…

Ios应用网络安全之https
戴维营教育原创文章,转载请注明出处。我们的梦想是做最好的iOS开发培训!iOS应用网络安全之HTTPS1. HTTPS/SSL的基本原理安全套接字层 (Secure Socket Layer, SSL) 是用来实现互联网安全通信的最普遍的标准。Web 应用程序使用 HTTPS(基于 SSL …

云原生应用的10大关键属性
2019独角兽企业重金招聘Python工程师标准>>> “云原生”是用于描述基于容器的环境的术语,而Kubernetes是一个运行云原生应用程序工作负载的理想平台。 开发人员在设计云原生应用程序时,一定要牢记本文内这10个关键属性! “云原生&…

grep 正则表达式及选项以及注意
说明:在原文基础上稍作了修改grep命令简介: 在ex编辑器(我没用过)中,启动ex编辑器后要查找某个字符串时,在ex的命令提示符后键入::/pattern/p:/g/pattern/pgrep这个名字就由来如此。其中p的含义是print&…