深入理解 python 虚拟机:字节码灵魂——Code obejct

news2024/11/24 13:59:09

Code Object 数据结构

typedef struct {
PyObject_HEAD
int co_argcount; /* #arguments, except *args */
int co_kwonlyargcount; /* #keyword only arguments */
int co_nlocals; /* #local variables */
int co_stacksize; /* #entries needed for evaluation stack */
int co_flags; /* CO_..., see below */
PyObject *co_code; /* instruction opcodes */
PyObject *co_consts; /* list (constants used) */
PyObject *co_names; /* list of strings (names used) */
PyObject *co_varnames; /* tuple of strings (local variable names) */
PyObject *co_freevars; /* tuple of strings (free variable names) */
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
/* The rest aren't used in either hash or comparisons, except for
co_name (used in both) and co_firstlineno (used only in
comparisons). This is done to preserve the name and line number
for tracebacks and debuggers; otherwise, constant de-duplication
would collapse identical functions/lambdas defined on different lines.
*/
unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
PyObject *co_filename; /* unicode (where it was loaded from) */
PyObject *co_name; /* unicode (name, for reference) */
int co_firstlineno; /* first source line number */
PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See
Objects/lnotab_notes.txt for details. */
void *co_zombieframe; /* for optimization only (see frameobject.c) */
PyObject *co_weakreflist; /* to support weakrefs to code objects */
} PyCodeObject;

下面是 code object 当中各个字段的作用:

  • 首先需要了解一下代码块这个概念,所谓代码块就是一个小的 python 代码,被当做一个小的单元整体执行。在 python 当中常见的代码块块有:函数体、类的定义、一个模块。

  • argcount,这个表示一个代码块的参数个数,这个参数只对函数体代码块有用,因为函数可能会有参数,比如上面的 pycdemo.py 是一个模块而不是一个函数,因此这个参数对应的值为 0 。

  • co_code,这个对象的具体内容就是一个字节序列,存储真实的 python 字节码,主要是用于 python 虚拟机执行的,在本篇文章当中暂时不详细分析。

  • co_consts,这个字段是一个列表类型的字段,主要是包含一些字符串常量和数值常量,比如上面的 "__main__" 和 100 。

  • co_filename,这个字段的含义就是对应的源文件的文件名。

  • co_firstlineno,这个字段的含义为在 python 源文件当中第一行代码出现的行数,这个字段在进行调试的时候非常重要。

  • co_flags,这个字段的主要含义就是标识这个 code object 的类型。0x0080 表示这个 block 是一个协程,0x0010 表示这个 code object 是嵌套的等等。

  • co_lnotab,这个字段的含义主要是用于计算每个字节码指令对应的源代码行数。

  • co_varnames,这个字段的主要含义是表示在一个 code object 本地定义的一个名字。

  • co_names,和 co_varnames 相反,表示非本地定义但是在 code object 当中使用的名字。

  • co_nlocals,这个字段表示在一个 code object 当中本地使用的变量个数。

  • co_stackszie,因为 python 虚拟机是一个栈式计算机,这个参数的值表示这个栈需要的最大的值。

  • co_cellvars,co_freevars,这两个字段主要和嵌套函数和函数闭包有关,我们在后续的文章当中将详细解释这个字段。

CodeObject 详细分析

现在我们使用一些实际的例子来分析具体的 code object 。

 
import dis
import binascii
import types
d = 10
def test_co01(c):
a = 1
b = 2
return a + b + c + d

在前面的文章当中我们提到过一个函数是包括一个 code object 对象,test_co01 的 code object 对象的输出结果(完整代码见co01)如下所示:

 
code
argcount 1
nlocals 3
stacksize 2
flags 0043 0x43
code b'6401007d01006402007d02007c01007c0200177c0000177400001753'
9 0 LOAD_CONST 1 (1)
3 STORE_FAST 1 (a)
10 6 LOAD_CONST 2 (2)
9 STORE_FAST 2 (b)
11 12 LOAD_FAST 1 (a)
15 LOAD_FAST 2 (b)
18 BINARY_ADD
19 LOAD_FAST 0 (c)
22 BINARY_ADD
23 LOAD_GLOBAL 0 (d)
26 BINARY_ADD
27 RETURN_VALUE
consts
None
1
2
names ('d',)
varnames ('c', 'a', 'b')
freevars ()
cellvars ()
filename '/tmp/pycharm_project_396/co01.py'
name 'test_co01'
firstlineno 8
lnotab b'000106010601'
  • 字段 argcount 的值等于 1,说明函数有一个参数,这个函数 test_co01 有一个参数 c 是相互对应的。
  • 字段 nlocals 的值等于 3,说明在函数 test_co01 当中一个一共实现了三个函数本地变量 a, b, c 。
  • 字段 names,对应代码代码当中的 co_names,根据前面的定义就是 d 这个全局变量在函数 test_co01 当中使用,但是却没有在函数当中定义了。
  • 字段 varnames,这个就表示在本地定义使用的变量了,在函数 test_co01 当中主要有三个变量 a, b, c 。
  • 字段 filename,就是 python 文件的地址了。
  • 字段 firstlineno 说明函数的第一行出现在对应 python 代码的 第 8 行。

Flags 字段详细分析

我们具体使用 python3.5 的源代码进行分析,在 cpython 虚拟机的具体实现如下所示(Include/code.h):

 
/* Masks for co_flags above */
#define CO_OPTIMIZED 0x0001
#define CO_NEWLOCALS 0x0002
#define CO_VARARGS 0x0004
#define CO_VARKEYWORDS 0x0008
#define CO_NESTED 0x0010
#define CO_GENERATOR 0x0020
/* The CO_NOFREE flag is set if there are no free or cell variables.
This information is redundant, but it allows a single flag test
to determine whether there is any extra work to be done when the
call frame it setup.
*/
#define CO_NOFREE 0x0040
/* The CO_COROUTINE flag is set for coroutine functions (defined with
``async def`` keywords) */
#define CO_COROUTINE 0x0080
#define CO_ITERABLE_COROUTINE 0x0100

如果 flags 字段和上面的各个宏定义进行 & 运算,如果得到的结果大于 0,则说明符合对应的条件。

上面的宏定义的含义如下所示:

  • CO_OPTIMIZED,这个字段表示 code object 是被优化过的,使用函数本地定义的变量。

  • CO_NEWLOCALS,这个字段的含义为当这个 code object 的代码被执行的时候会给栈帧当中的 f_locals 对象创建一个 dict 对象。

  • CO_VARARGS,表示这个 code object 对象是否含有位置参数。

  • CO_VARKEYWORDS,表示这个 code object 是否含有关键字参数。

  • CO_NESTED,表示这个 code object 是一个嵌套函数。

  • CO_GENERATOR,表示这个 code object 是一个生成器。

  • CO_COROUTINE,表示这个 code object 是一个协程函数。

  • CO_ITERABLE_COROUTINE,表示 code object 是一个可迭代的协程函数。

  • CO_NOFREE,这个表示没有 freevars 和 cellvars,即没有函数闭包。

现在再分析一下前面的函数 test_co01 的 flags,他对应的值等于 0x43,则说明这个函数满足三个特性分别是 CO_NEWLOCALS,CO_OPTIMIZED 和 CO_NOFREE。

freevars & cellvars

我们使用下面的函数来对这两个字段进行分析:

 
def test_co02():
a = 1
b = 2
def g():
return a + b
return a + b + g()

上面的函数的信息如下所示(完整代码见co02):

 
code
argcount 0
nlocals 1
stacksize 3
flags 0003 0x3
code
b'640100890000640200890100870000870100660200640300640400860000'
b'7d0000880000880100177c00008300001753'
15 0 LOAD_CONST 1 (1)
3 STORE_DEREF 0 (a)
16 6 LOAD_CONST 2 (2)
9 STORE_DEREF 1 (b)
18 12 LOAD_CLOSURE 0 (a)
15 LOAD_CLOSURE 1 (b)
18 BUILD_TUPLE 2
21 LOAD_CONST 3 (<code object g at 0x7f133ff496f0, file "/tmp/pycharm_project_396/co01.py", line 18>)
24 LOAD_CONST 4 ('test_co02.<locals>.g')
27 MAKE_CLOSURE 0
30 STORE_FAST 0 (g)
20 33 LOAD_DEREF 0 (a)
36 LOAD_DEREF 1 (b)
39 BINARY_ADD
40 LOAD_FAST 0 (g)
43 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
46 BINARY_ADD
47 RETURN_VALUE
consts
None
1
2
code
argcount 0
nlocals 0
stacksize 2
flags 0013 0x13
code b'8800008801001753'
19 0 LOAD_DEREF 0 (a)
3 LOAD_DEREF 1 (b)
6 BINARY_ADD
7 RETURN_VALUE
consts
None
names ()
varnames ()
freevars ('a', 'b')
cellvars ()
filename '/tmp/pycharm_project_396/co01.py'
name 'g'
firstlineno 18
lnotab b'0001'
'test_co02.<locals>.g'
names ()
varnames ('g',)
freevars ()
cellvars ('a', 'b')
filename '/tmp/pycharm_project_396/co01.py'
name 'test_co02'
firstlineno 14
lnotab b'0001060106021502'

从上面的输出我们可以看到的是,函数 test_co02 的 cellvars 为 ('a', 'b'),函数 g 的 freevars 为 ('a', 'b'),cellvars 表示在其他函数当中会使用本地定义的变量,freevars 表示本地会使用其他函数定义的变量。

再来分析一下函数 test_co02 的 flags,他的 flags 等于 0x3 因为有闭包的存在因此 flags 不会存在 CO_NOFREE,也就是少了值 0x0040 。

stacksize

这个字段存储的是在函数在被虚拟机执行的时候所需要的最大的栈空间的大小,这也是一种优化手段,因为在知道所需要的最大的栈空间,所以可以在函数执行的时候直接分配指定大小的空间不需要在函数执行的时候再去重新扩容。

 
def test_stack():
a = 1
b = 2
return a + b

上面的代码相关字节码等信息如下所示:

 
code
argcount 0
nlocals 2
stacksize 2
flags 0043 0x43
code b'6401007d00006402007d01007c00007c01001753'
# 字节码指令 # 字节码指令参数 # 参数对应的值
24 0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (a)
25 6 LOAD_CONST 2 (2)
9 STORE_FAST 1 (b)
26 12 LOAD_FAST 0 (a)
15 LOAD_FAST 1 (b)
18 BINARY_ADD
19 RETURN_VALUE
consts
None # 下标等于 0 的常量
1 # 下标等于 1 的常量
2 # 下标等于 2 的常量
names ()
varnames ('a', 'b')
freevars ()
cellvars ()

我们现在来模拟一下执行过程,在模拟之前我们首先来了解一下上面几条字节码的作用:

  • LOAD_CONST,将常量表当中的下标等于 i 个对象加载到栈当中,对应上面的代码 LOAD_CONST 的参数 i = 1。因此加载测常量等于 1 。因此现在栈空间如下所示:

  • STORE_FAST,将栈顶元素弹出并且保存到 co_varnames 对应的下标当中,根据上面的字节码参数等于 0 ,因此将 1 保存到 co_varnames[0] 对应的对象当中。

  • LOAD_CONST,将下标等于 2 的常量加载进入栈中。

  • STORE_FAST,将栈顶元素弹出,并且保存到 varnames 下标为 1 的对象。

  • LOAD_FAST,是取出 co_varnames 对应下标的数据,并且将其压入栈中。我们直接连续执行两个 LOAD_FAST 之后栈空间的布局如下:

  • BINARY_ADD,这个字节码指令是将栈空间的两个栈顶元素弹出,然后将两个数据进行相加操作,然后将相加得到的结果重新压入栈中。

  • RETURN_VALUE,将栈顶元素弹出并且作为返回值返回。

从上面的整个执行过程来看整个栈空间使用的最大的空间长度为 2 ,因此 stacksize = 2 。

总结

在本篇文章当中主要分析了一些 code obejct 当中比较重要的字段,code object 是 cpython 虚拟机当中一个比较重要的数据结构,深入的去理解这里面的字段对于我们理解 python 虚拟机非常有帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/796124.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter 异步编程指南作

1 Dart 中的事件循环模型 在 App 开发中&#xff0c;经常会遇到处理异步任务的场景&#xff0c;如网络请求、读写文件等。Android、iOS 使用的是多线程&#xff0c;而在 Flutter 中为单线程事件循环&#xff0c;如下图所示 Dart 中有两个任务队列&#xff0c;分别为 microtask…

Windows 11的最新人工智能应用Windows Copilot面世!

Windows Copilot是Windows 11预览版中的一项AI辅助功能。 Windows 11还包括设置应用程序的更改&#xff0c;更广泛的支持压缩文件格式。 上个月&#xff0c;微软宣布将继续其将ChatGPT应用于所有产品的冒险之旅&#xff0c;推出了名为Copilot的新Windows 11功能。几个月前&…

难评的Worldcoin,正在登月中

7月24日&#xff0c;前段时间搅混加密舆论界一汪春水的Worldcoin正式上线代币WLD。 由Openai创始人Sam Altman牵头的Worldcoin项目在其试点之初就备受关注&#xff0c;而该种关注在其融资近一亿美金后更是空前&#xff0c;币种上架的呼声不断。 但当代币真的狼来了时&#xff0…

使用CRM分析数据有哪些功能?

CRM数据分析软件可以帮助企业增强竞争力&#xff0c;并更好地了解客户需求及市场变化&#xff0c;助力企业数据分析&#xff0c;并提供实时更新的数据和分析结果&#xff0c;CRM数据分析软件的主要特点是什么&#xff1f;包括以下6个特点。 CRM数据分析软件的主要功能通常包括…

四、约束-2.演示

【案例】 根据需求&#xff0c;完成表结构的创建 创建一个user表&#xff1a; create table user(id int primary key auto_increment comment 主键,name varchar(10) not null unique comment 姓名,age int check ( age > 0 && age < 120 ) comment 年龄,statu…

《嵌入式系统工程师》精讲视频-希赛网--视频笔记

只看我不熟的内容 P8 04流水线 理论公式必须熟悉 P29 01OSI&#xff0f;RM 1年大概考4分&#xff0c;主要考察前3个知识点 应用层--与用户打交道&#xff1b;表示层--压缩、加密等&#xff1b;会话层--建立、终止会话&#xff1b; 传输层--建立端到端连接&#xff1b;网络层--路…

vue-cli-service requires Node ^12.0.0 || >= 14.0.0

运行npm run serve 时&#xff0c;报错如下&#xff1a; 系统&#xff1a;win7 node版本&#xff1a;13.14.0 解决方法&#xff1a; 根据路径&#xff08;node_modules/vue/cli-service/package.json&#xff09;找到package.json文件&#xff0c;并将engines的node属性值改为…

关于Ubuntu 18.04 LTS环境下运行程序出现的问题

关于Ubuntu 18.04 LTS环境下运行程序出现的问题 1.运行程序时出现以下情况 2.检查版本 strings /lib/x86_64-linux-gnu/libc.so.6 |grep GLIBC_​ 发现Ubuntu18.04下的glibc版本最高为2.27,而现程序所使用的是glibc2.34,所以没办法运行, 3.解决办法 安装glibc2.34库, …

C语言每日一题:7.寻找数组中心下标。

思路一&#xff1a; 暴力求解&#xff1a; 1.定义一个ps作为中间下标去记录下标值。 2.循环下标ps从头到位&#xff0c;定义四个变量分别是left sum_left,right,sum_right… 3.初始化leftps-1和rightps1.当ps0—>就让sum_left0,和psn-1->>>sum_right0; 4.循环结尾判…

深入探索文心千帆大模型平台:实现企业级大模型训练和推理

摘要&#xff1a;本文将介绍百度智能云推出的文心千帆大模型平台&#xff0c;以满足企业和个人客户的需求。通过该平台&#xff0c;用户可以进行大模型训练和推理&#xff0c;并且享受一站式的工具链和环境。作者将分享自己在平台上的亲身体验&#xff0c;并提供相关的代码示例…

阿里云RDS数据库高可用版升级为集群版

文章目录 1.在集群中添加两个只读实例1.1.点击添加只读实例1.2.配置实例的计费方式及所在可用区1.3.设置实例的资源规格1.4.选择实例使用的专有网络1.5.确认参数配置及订单信息1.6.查看购买的只读实例 2.开启RDS数据库的读写分离2.1.查看只读实例提供的集群地址2.2.开启RDS的读…

算法(2)

二叉树 镜像二叉树 树轴对称 第一个节点的左子树与第二个节点的右子树同步递归对比&#xff0c;第一个节点的右子树与第二个节点的左子树同步递归比较。 二叉树序列化、反序列化 当然你也可以根据满二叉树结点位置的标号规律来序列化&#xff0c;还可以根据先序遍历和中序遍…

客户体验:妙鸭相机(AI)与线下摄影的体验比较

Guofu 第 103⭐️ 篇原创文章分享 &#xff08;点击&#x1f446;&#x1f3fb;上方卡片关注我&#xff0c;加⭐️星标⭐️~&#xff09; &#x1f68f; 写在前面 最近 AI 的发展&#xff0c;每天都在刷新着我们的认知&#xff0c;人工智能已经渗透到我们生活的各个角落。其中&…

ssh2-sftp-client实现前端项目自动部署

首先要npm安装插件 npm i ssh2-sftp-client 项目中新建一个js文件 npm run build 之后在终端中 执行这个js文件就可以直接将文件上传到 服务器 import Client from ssh2-sftp-client; import { join } from path;const sftp new Client();const deploy async () > {try…

内存管理:判断对象是否存活

引用计数算法 引用计数算法&#xff08;Reference Counting&#xff09;判断对象是否存活的基本思路是&#xff1a;在对象中添加一个引用计数器&#xff0c;每当有一个地方引用该对象时&#xff0c;计数器的值就加一&#xff1b;当引用失效时&#xff0c;计数器的值就减一&…

Vector - CAPL - 诊断模块函数(TP层配置)

诊断TP层常见参数值 网络层定时参数的放置 常见的时间参数在诊断数据交互中的位置&#xff0c;后续会专门出一期关于ISO 15765-2的介绍。 CAPL对于此类函数介绍 CanTpGetTimeoutAr & CanTpGetTimeoutAr long CanTpGetTimeoutAr(long connHandle); long CanTpSetTimeoutAr(…

RabbitMQ 教程 | RabbitMQ 简介

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

充分了解java阻塞队列机制

多线程基础 1.阻塞队列1.1 什么是 阻塞队列1.2 阻塞队列的特点 1.3 阻塞队列常用方法1.3.1 抛出异常:add、remove、element1.3.2 返回结果但是不抛出异常offer、poll、peek1.3.3 阻塞put和take1.3.4 小结 1.4 常见的阻塞队列1.4.1 ArrayListBlockingQueue1.4.2 LinkedBlockingQ…

国标GB28181协议视频平台EasyCVR修改录像计划等待时间较长的原因排查与解决

音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、H.265自动转码H.264、平台级联等。为了便于用户二次开发、调用与集成&…

【PMP】有没有项目经理能看得懂这九张图?求挑战

这九张图&#xff0c;全是圈圈我的肺腑之言啊&#xff01;谁痛谁知道&#xff01; 做技术时&#xff0c;就想着30岁就转管理&#xff0c;管理岗位赚得多&#xff0c;结果发现全是烟雾弹。 做技术和代码打交道&#xff0c;做管理跟人打交道。天天开不完的会、说不完的话&#xf…