Python中的 LOAD_DEREF LOAD_CLOSURE

LOAD_DEREF

在Python(特别是CPython实现)的字节码指令集中,LOAD_DEREF 是一个操作码,用于从函数的闭包(如果存在)或从当前函数的局部作用域外的命名空间(enclosing scope)中加载一个变量。

这一点在处理嵌套作用域和闭包(closures)时特别重要。当一个内部函数引用了一个在外部函数中定义的变量,这个变量就会被视为一个“自由变量”(free variable)。LOAD_DEREF 指令用于在内部函数中加载这种自由变量。

假设我们有以下Python代码:

def outer():x = 10def inner():print(x)return innerfunc = outer()
func()

在这里,inner 是一个闭包,因为它引用了外部作用域(即 outer 函数)中的变量 x。当 inner 被调用时,它需要能够访问 x,即使 outer 的执行已经完成。这就是 LOAD_DEREF 指令发挥作用的地方。

如果我们用Python的 dis 模块来反汇编 inner 函数,我们可能会看到类似以下内容的字节码:

  5           0 LOAD_GLOBAL              0 (print)2 LOAD_DEREF               0 (x)4 CALL_FUNCTION            16 POP_TOP8 LOAD_CONST               0 (None)10 RETURN_VALUE

注意在第2行中的 LOAD_DEREF 指令。这个指令加载了在 inner 函数外部定义的变量 x

LOAD_CLOSURELOAD_FASTLOAD_GLOBAL 等其他字节码指令一样,LOAD_DEREF 是CPython中的实现细节,并不是Python语言规范的一部分。不同的Python实现(比如PyPy, Jython等)可能会有不同的方式来实现相同的功能。

LOAD_CLOSURE

在Python的字节码层面,LOAD_CLOSURE 是一个操作码(opcode)用于实现闭包(closures)的功能。这个操作码负责从当前函数的闭包(closure)中加载一个自由变量(free variable)。

Python中的函数可以引用定义在它们外部作用域的变量。当一个函数引用了这样的外部变量并且被当做对象返回,这个函数就被称为闭包。这些外部作用域的变量称为自由变量。

LOAD_CLOSURE 指令在函数的字节码中出现,用于从该函数的闭包中获取一个自由变量,并将其压入操作数栈顶。这使得该函数能在稍后执行时访问这个自由变量。

举个简单的例子:

def outer(x):def inner(y):return x + yreturn inner# inner 函数是一个闭包,它引用了 outer 函数的局部变量 x

在这个例子中,inner 函数是一个闭包,因为它访问了外部作用域(outer 函数)的变量 x。如果你查看 inner 函数的字节码(通过内置的 dis 模块),你可能会看到 LOAD_CLOSURE 指令,用于加载 x 这个自由变量。

要注意的是,Python字节码并非Python语言规范的一部分,而是CPython解释器(Python的一个实现)的内部细节。因此,其他Python实现(如PyPy、Jython等)可能没有 LOAD_CLOSURE 这一操作码,或者实现闭包的方式可能不同。

LOAD_CLOSURE和LOAD_DEREF的区别

LOAD_CLOSURELOAD_DEREF 都是与Python(特别是CPython实现)中的闭包和自由变量相关的字节码指令,但它们在闭包构建和运行时具有不同的用途。

LOAD_CLOSURE

  • 用途: 当你创建一个闭包(也就是说,一个内部函数引用了外部作用域中的一个或多个变量)时,LOAD_CLOSURE 被用于获取这些外部变量,也称为自由变量。
  • 时机: 这个指令通常在闭包函数对象被创建时执行。
  • 功能: 它将自由变量加载到闭包对象的环境中,以便稍后使用。

LOAD_DEREF

  • 用途: 在闭包内部运行时,当需要访问自由变量时,使用 LOAD_DEREF 指令。
  • 时机: 这个指令在闭包被调用并需要访问一个或多个自由变量时执行。
  • 功能: 它从闭包对象的环境中取出自由变量,并将其值压入操作数栈顶,以便在闭包内部进行其他操作(例如,运算或函数调用)。

简单来说,LOAD_CLOSURE 在闭包创建时建立自由变量的环境,而 LOAD_DEREF 在闭包运行时从这个环境中读取自由变量。

例子

考虑以下代码:

def outer(x):def inner(y):return x + yreturn inner

在这里,inner 是一个闭包,它引用了外部(outer)的变量 x

  • inner 被定义(即创建闭包)时,LOAD_CLOSURE 指令将用于捕获 x 的当前值或引用,并将其存储在闭包的环境中。
  • inner 被调用并执行 return x + y 时,LOAD_DEREF 指令用于从闭包的环境中获取 x 的值。

这样,即使 outer 的执行已经完成,inner 仍然可以访问 x 的值。这就是闭包如何“记住”其外部变量的环境的基本机制。

请注意,这些都是CPython的实现细节,并不是Python语言规范的一部分。其他Python实现可能有不同的方式来实现这些功能。

疑问

为什么在 inner 被定义(即创建闭包)时,LOAD_CLOSURE 指令捕获了 x,在调用并执行 return x + y 时,LOAD_DEREF 指令还要从闭包的环境中再获取一次 x 的值呢 ?

回答

在Python中创建和调用闭包时,LOAD_CLOSURELOAD_DEREF 的分离主要是为了满足两个不同阶段的需求:

创建闭包时的需求(LOAD_CLOSURE

  1. 环境捕获:闭包需要“捕获”它引用的自由变量的当前环境。这样即使外部函数(如 outer)的执行上下文已经消失,闭包仍然能够访问这些变量。

  2. 存储状态:使用 LOAD_CLOSURE 指令,解释器将自由变量(如 x)与闭包(inner)关联,并存储在一个特殊的数据结构中,通常称为“单元”(cell)。

调用闭包时的需求(LOAD_DEREF

  1. 动态查找:当闭包(inner)被调用时,它可能需要基于最新的变量状态进行操作。使用 LOAD_DEREF,闭包可以动态地从其存储的环境中获取自由变量的当前值。

  2. 运行时需求:闭包的执行逻辑可能依赖于这些自由变量,所以在运行时需要加载这些变量。LOAD_DEREF 指令从闭包的环境中取出这些变量,并将其值压入操作数栈以进行后续操作。

为什么需要两步?

简而言之,LOAD_CLOSURELOAD_DEREF 分离成两个步骤主要是为了灵活性和动态性。

  • 灵活性:这种设计允许闭包在其创建之后的任何时间点被调用,而不管其外部环境是否还存在。这是通过在闭包创建时捕获和存储自由变量的环境来实现的。

  • 动态性:Python是一种动态语言,这意味着在运行时可以进行很多操作,包括但不限于动态地改变变量的值。LOAD_DEREF 允许闭包在每次调用时都能够访问其自由变量的最新值。

这两个指令共同允许Python闭包在运行时保持对外部作用域(enclosing scope)变量的实时访问,同时也保留了这些变量的状态,即使它们的原始作用域已经不存在。

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

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

相关文章

JavaScript-DOM查询

获取元素节点 获取元素节点的子节点 元素节点的属性 节点的修改 JavaScript中的DOM(文档对象模型)是一种编程接口,它允许JavaScript与HTML文档交互。创建DOM查询,可以使用多种方法. 获取元素节点 1. getElementById() – 通…

【C语言进阶(8)】自定义数据类型1:结构体

文章目录 前言Ⅰ 结构体的声明和定义⒈结构体声明⒉结构体定义⒊特殊的声明 Ⅱ 结构体的自引用Ⅲ 结构体初始化Ⅳ 访问结构体成员Ⅴ 结构体内存对齐⒈结构体内存对齐规则⒉分析结构体大小⒊嵌套结构体内存大小⒋内存对齐存在的原因 Ⅵ 修改默认对齐数Ⅶ 结构体传参 前言 C 语言…

js toFixed()方法的坑

最近发现JS当中toFixed()方法存在一些问题,采用原生的Number对象的原型对象上的toFixed()方法时,规则并不是所谓的“四舍五入”或者是“四舍六入五成双”,以谷歌浏览器为例: alert(1.5451.toFixed(2));//输出 1.55 alert(1.545.toFixed(2));…

围棋与育种

最近因为娃子报名了围棋课,我本着交一次课学两个人的态度,入门围棋,买了些书,听了些课,有所得。学了两个多月,现在6级水平,了解了基本的攻杀技巧,会判断输赢。 下面截图是今天的一盘…

Mr. Cappuccino的第64杯咖啡——Spring循环依赖问题

Spring循环依赖问题 什么是循环依赖问题示例项目结构项目代码运行结果 Async注解导致的问题使用Lazy注解解决Async注解导致的问题开启Aop使用代理对象示例项目结构项目代码运行结果 Spring是如何解决循环依赖问题的原理源码解读 什么情况下Spring无法解决循环依赖问题 什么是循…

IDEA配置热启动

1.背景 开发过程中,当写完一个功能我们需要运行应用程序测试,可能这个小功能中存在多个小bug,我们需要改正后重启服务器,这无形之中拖慢了开发的速度增加了开发时间,SpringBoot提供了spring-boot-devtools,…

MyBatis分页查询与特殊字符处理

目录 目录 一、引言 1.1 简介Mybatis 1.2分页查询的重要性 1.3MyBatis特殊字符处理的挑战 挑战1:SQL注入漏洞 挑战2:查询结果异常 挑战3:数据完整性问题 挑战4:跨平台兼容性 挑战5:用户体验 如何应对挑战 二…

Flask-Sockets和Flask-Login联合实现websocket的登录认证功能

flask_login 提供了一个方便的方式来管理用户会话。当你在 Flask 的 HTTP 视图中使用它时,你可以简单地使用 login_required 装饰器来确保用户已登录。 但是,flask_sockets 并没有直接与 flask_login 集成。如果你想在建立 WebSocket 连接时检查用户是否…

4.14 HTTPS 中 TLS 和 TCP 能同时握手吗?

目录 实现HTTPS中TLS和TCP同时握手的前提: 什么是TCP Fast Open? TLS v1.3 TCP Fast Open TLSv1.3 HTTPS都是基于TCP传输协议实现的,得先建立完可靠得TCP连接才能做TLS握手的事情。 实现HTTPS中TLS和TCP同时握手的前提: 1、…

uniapp国际化npm install vue-i18n报错

npm install vue-i18n //npmyarn add vue-i18n //yarn在vue2环境下,默认安装 npm install vue-i18n 的版本是 vue-i18n9.1.9,所以报错。 npm view vue-i18n versions --json 用以上命令查看版本: vue2建议5.0版本 npm install vue-i1…

nacos适配SqlServer、Oracle

继上文《nacos适配达梦、瀚高、人大金仓数据库及部分源码探究 》后补充nacos适配SqlServer、Oracle的贴码,主要区别是SqlServer、Oracle的分页SQL有点不一样,做个记录; SqlServer的分页有三种实现方式:offset /fetch next、利用ma…

JAVA-编程基础-10-集合

Lison <dreamlison163.com>, v1.0.0, 2023.04.23 JAVA-编程基础-10-集合 文章目录 JAVA-编程基础-10-集合List、Set、Map、队列全面解析ListArrayList创建ArrayList 向ArrayList中添加元素 List、Set、Map、队列全面解析 Java 集合框架可以分为两条大的支线&#xff1a;…

uniapp 项目实践总结(一)uniapp 框架知识总结

导语&#xff1a;最近开发了一个基于 uniapp 框架的项目&#xff0c;有一些感触和体会&#xff0c;所以想记录以下一些技术和经验&#xff0c;在这里做一个系列总结&#xff0c;算是对自己做一个交代吧。 目录 简介全局文件全局组件常用 API条件编译插件开发 简介 uniapp 是…

【C++】开源:Box2D动力学库配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Box2D动力学库配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c…

Zenity 简介

什么使 Zenity Zenity 是一个开源的命令行工具&#xff0c;它提供了一种简单的方式来创建图形化的用户界面&#xff08;GUI&#xff09;对话框&#xff0c;以与用户进行交互。它基于 GTK 库&#xff0c;可以在 Linux 和其他 UNIX-like 系统上使用。 Zenity 可以通过命令行或脚…

Unity Alembic闪烁问题

最近在做项目时&#xff0c;发现Clo3D导出的服装abc动画&#xff0c;导入到Unity中后(已提前导入Alembic插件)&#xff0c;运行时屏幕会闪烁(变黑)。 经过几轮测试&#xff0c;发现是切线的问题。解决办法很简单。将abc文件上的Tangents属性值改为None即可。

【自适应稀疏度量方法和RQAM】疏度测量、RQAM特征、AWSPT和基于AWSPT的稀疏度测量研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

“之江创客”走进美洲 助推电商国际合作交流

“之江创客” 2023全球电子商务 创业创新大赛 美洲赛区决赛 当地时间8月24日下午&#xff0c;“之江创客”2023全球电子商务创业创新大赛美洲赛区决赛在墨西哥蒙特雷科技大学墨西哥城校区圆满落幕。浙江省商务厅党组成员、副厅长张钱江作视频致辞&#xff0c;蒙特雷科技大学…

深度对话:从底层看Sui设计理念及网络规模扩展

近日&#xff0c;我们采访了George Danezis以了解Sui的交易处理系统如何促成高性能网络。他是Mysten Labs的联合创始人和首席科学家&#xff08;Sui的最初贡献者&#xff09;&#xff0c;也是伦敦大学学院&#xff08;University College London&#xff0c;UCL&#xff09;安全…

849. 到最近的人的最大距离(JavaScript)849. Maximize Distance to Closest Person

给你一个数组 seats 表示一排座位&#xff0c;其中 seats[i] 1 代表有人坐在第 i 个座位上&#xff0c;seats[i] 0 代表座位 i 上是空的&#xff08;下标从 0 开始&#xff09;。 至少有一个空座位&#xff0c;且至少有一人已经坐在座位上。 亚历克斯希望坐在一个能够使他与…