遍历 locals() 时可能产生的 RuntimeError

news/2025/1/31 15:44:13/

文章目录

  • 参考
  • 描述
  • locals() 函数
  • 双面间谍:locals()
      • 静态 locals()
          • 获取最新变化
          • 保持同步
      • 动态 locals()
  • For Loop 过程中产生的迭代变量
  • 遍历 locals() 时可能产生的 RuntimeError
      • RuntimeError
      • 异常产生原因
      • 解决方案
          • copy 方法
          • 绕过 RuntimeError
      • 产生 RuntimeError 异常的基本要求
      • 遍历 globals() 时必不可少的 RuntimeError

参考

描述

locals() 函数

在 Python 中,locals() 是一个 内置函数,用于返回 当前作用域的命名空间的字典,该字典存储了当前作用域中定义的所有全局变量和函数。
locals() 函数返回一个字典,其中包含局部作用域中的变量名及其对应的值。这个字典可以用于 访问、修改和删除 当前作用域中的变量。

对此,请参考如下示例:

# 当在全局作用域中调用 locals() 函数时,
# 该函数将返回全局命名空间字典,与 globals()
# 返回的值相同。
print(locals() == globals())def func():# 当在函数中调用 locals() 函数时,# 该函数将返回其所在函数的局部命名空间字典。local_var = 'Hello World'print(locals())func()

执行效果

True
{'local_var': 'Hello World'}

双面间谍:locals()

静态 locals()

局部作用域下的 locals() 返回的是与局部作用域相关联的命名空间字典,该字典是静态的。这意味着,字典中的内容并不与实际命名空间中的变化保持同步。对此,请参考如下示例:

def func():first_locals = locals()# 此时局部命名空间中已注册 first_locals 变量# 但该变量并不会体现在 first_locals 中print(first_locals)a = 1# 此时局部命名空间中已注册 a 变量# 但该变量并不会体现在 first_locals 中print(first_locals)func()

执行效果

{}
{}
获取最新变化

在 Python 中,你可以通过在局部作用域中调用 locals() 来更新与局部命名空间相关联的字典,使其内容与局部命名空间中的内容保持一致。对此,请参考如下示例:

def func():first_locals = locals()print(first_locals)a = 1print(first_locals)# 调用 locals() 以更新与局部命名空间# 相关联的字典。locals()print(first_locals)func()

执行效果

{}
{}
{'first_locals': {...}, 'a': 1}
保持同步

为了时刻获取当前状态下局部命名空间中的内容,你应该总是使用 locals() 函数来获取与局部命名空间相关联的字典,而不是使用 locals() 的赋值结果。对此,请参考如下示例:

def func():print(locals())a = 1print(locals())func()

执行效果

{}
{'a': 1}

动态 locals()

全局作用域下的 locals() 返回的是与全局作用域相关联的命名空间字典,该字典是动态的(这一点与 Python 提供的 globals() 内置函数是相同的)。这意味着,字典中的内容时刻与实际命名空间中的变化保持同步。对此,请参考如下示例:

first_locals = locals()# 判断全局变量 a 是否存在于 first_locals 中
print('a' in first_locals)# 定义全局变量 a
a = 1print('a' in first_locals)

执行效果

False
True

For Loop 过程中产生的迭代变量

在 Python 的 for 循环 中,迭代变量 是指在每次迭代中 用于表示可迭代对象中的当前元素的变量。迭代变量的名称是根据程序员的选择来确定的,它可以是 任意有效的变量名

举个栗子

# 在下面的 For Loop 中,迭代变量为 num
for num in range(100):pass# 在 For Loop 过程中,将会在当前作用域下
# 创建迭代变量。因此在 For Loop 结束后,
# 仍能够在当前作用域中访问到该变量。
print(num)

执行效果

99

遍历 locals() 时可能产生的 RuntimeError

RuntimeError

当你在 Python 的局部作用域中尝试遍历 locals() 函数的返回结果时,可能会产生 RuntimeError。对此,请参考如下示例:

def func():china = 'Hello China'for i in locals():locals()func()

执行效果

执行上述代码后,将产生 RuntimeError 异常错误。其中包含如下内容

RuntimeError: dictionary changed size during iteration

异常产生原因

字典在内部使用 散列表(也被称为哈希表) 来实现 键值对存储查找。散列表的 工作原理 涉及将键通过哈希函数转换为索引,然后在存储桶中存取相应的值。
散列表的结构在 迭代过程 中是 敏感的。当你在迭代字典的 视图对象(字典提供的视图对象是动态的、实时反映字典内容变化的可迭代对象) 时,若 添加删除 字典中的条目,会导致 字典的结构发生变化
当你迭代字典提供的视图对象时,迭代器会维护一个 内部状态 来跟踪字典的条目。如果在迭代过程中修改字典的大小,迭代器的内部状态与字典的实际状态将不再一致。如果继续迭代,可能会产生意想不到的结果或错误的行为。为了保持迭代的一致性和正确性,Python 选择在这种情况下抛出 RuntimeError 异常,以提醒开发者字典发生了变化,并防止继续迭代可能导致的问题。

在迭代 locals() 函数时,可能将引发 RuntimeError。其原因是,在迭代过程中产生的迭代变量将注册至当前作用域中,若在循环体中调用 locals() ,使得 locals() 字典更新,将导致被迭代字典对象的结构发生改变,从而引发了 RuntimeError 异常错误。
如果被迭代对象为 locals(),并且你未在循环体中调用 locals() 函数,则被迭代对象的结构将不会由于迭代变量的产生而发生改变,因此 Python 也不会抛出 RuntimeError 异常。对此,请参考如下示例:

def func():china = 'Hello China'for i in locals():# 此处不调用 locals() 以更新被迭代字典对象passfunc()

执行效果

执行过程中,未产生任何异常错误。

解决方案

copy 方法

在 Python 中,字典对象具有 copy() 方法,用于创建字典的 浅拷贝
字典对象的 copy() 方法用于创建一个 的字典对象,其中包含原始字典的副本。该副本是一个独立的字典对象,具有相同的键值对。但是,它是一个 浅拷贝,意味着副本中的键值对是原始字典中相应键值对的 引用

举个栗子

d = {'member': ['RedHeart', 'BinaryMoon']}# 获取字典对象 d 的浅拷贝
shallow_d = d.copy()# 判断两个字典中的键值对的内容是否完全相同,
# 不考虑键值对的排列顺序。
print(d == shallow_d)# 判断原对象与其浅拷贝对象的身份标识是否相同,
# 对象的身份标识及其在内存空间中存放的内存地址信息。
print(d is shallow_d)# 尝试修改字典中的可变对象(列表)
d['member'].append('BlackOvercoat')# 由于浅拷贝后的数据结构中的元素是原数据结构的元素的引用,
# 因此可变对象的更改将影响其他浅拷贝中相应可变对象的更改。
print(d)
print(shallow_d)

执行效果

True
False
{'member': ['RedHeart', 'BinaryMoon', 'BlackOvercoat']}
{'member': ['RedHeart', 'BinaryMoon', 'BlackOvercoat']}
绕过 RuntimeError

在迭代过程中产生的迭代变量将注册至当前作用域的命名空间中。若在局部作用域中迭代 locals() 函数,将在局部命名空间中注册迭代变量,这会导致 locals() 函数发生改变,从而引发 RuntimeError。若我们迭代的是 locals() 的副本,即使迭代过程中产生的迭代变量可能会导致原对象发生改变,但这并不会影响到其相关的拷贝对象。

举个栗子

def func():china = 'Hello China'for i in locals().copy():locals()func()

执行效果

遍历对象由 locals() 改为 locals().copy() 后,执行上述代码,Python 将不会抛出 RuntimeError 异常错误。

产生 RuntimeError 异常的基本要求

在遍历 locals() 产生 RuntimeError 的一个基本要求是,在开始对 locals() 函数进行首次遍历时,locals() 函数的返回结果中存在 至少一个 的键值对。第一次迭代过程中将产生并在当前作用域下注册 迭代变量(Python 将从被迭代对象中取出一个值并赋值给迭代变量,若没有从被迭代对象中取出值则迭代变量不会被创建)

def func():for i in locals():# 由于首次遍历 locals() 时,# locals() 中不存在任何内容,因此# 循环体中的内容不会被执行,# 迭代变量也不会在当前作用域中注册。print('Hello World')locals()func()

执行效果

上述代码执行后,控制台中未输出任何内容。

遍历 globals() 时必不可少的 RuntimeError

在 Python 中,globals() 是一个 内置函数,用于返回 全局作用域的命名空间的字典,该字典存储了全局作用域中定义的所有对象。
globals() 函数返回一个字典,其中包含局部作用域中的变量名及其对应的值。这个字典可以用于 访问、修改和删除 当前作用域中的变量。

globals()locals() 函数的功能类似,这也意味着 globals() 也存在与 locals() 相同的问题,但 globals() 相比 locals() 来说,RuntimeError 异常触发的机制存在不同。如需对其进行了解,欢迎前往我的另一篇博客 遍历 globals() 时必不可少的 RuntimeError


http://www.ppmy.cn/news/84827.html

相关文章

回归测试概念和4种回归测试策略——你想知道的都在这里啦!

前言: 回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。自动回归测试将大幅降低系统测试、维护升级等阶段的成本。回归测试作为软件生命周期的一个组成部分,在整个软件测试过程中占有很大的工作…

如何做到设备维护事半功倍?

在工业生产过程中,设备故障可能导致生产停机、成本增加和安全风险。因此,及时监测设备的健康状况,预测潜在故障,并采取相应的维护措施至关重要。 振动在线监测系统是一种有效的工具,可以实时监测设备振动,并…

apache2 wsgi socket rotaion (by quqi99)

作者:张华 发表于:2023-05-22 版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 问题 for lp bug https://bugs.launchpad.net/ubuntu/source/mod-wsgi/bug/1863232 reproducer lxc l…

20230525设备树配置点灯

补充:查看设备驱动cat /proc/devices 查看字符设备文件ls /dev/mycdev 查看设备树节点文件:ls /proc/device-tree/myleds 设备树源码dts 设备树源码补充文件 dtsi 设备树镜像文件dtb 编译镜像make uimage 编译设备树make dtbs 编译为m的模块 make modlue…

【算法学习系列】05 - 对数器的说明与使用

文章目录 对数器说明对数器使用创建随机样本生成器实现 isSorted(int[] arr) 函数验证排序正确性实现选择排序算法进行大样本随机测试验证算法正确性 总结 对数器说明 在算法领域中,对数器指的是一个用于测试算法正确性的工具。对于一个需要被测试的算法A&#xff0…

当四款AI大模型遇上考公真题,谁被难倒了?

在当今社会,人工智能(AI)正以不可思议的速度发展,并在各个领域崭露头角,给人们的生活和工作带来许多便利。AI大模型被誉为人类“第二大脑”,成为人们学习、生活、工作的 “智能助手”。 公务员考试在我国教…

C语言入门篇——编译篇

目录 1、程序环境 1.1 ANSI C 标准 1.2程序的翻译环境和执行环境 1.3运行环境 2、预处理详解 2.1、预定义符号 2.2、#define 2.2.1#define定义表示符 2.2.2#define定义宏 2.2.3#define替换规则 2.4#和## 2.2.5带副作用的宏参数 2.2.6宏和函数对比 3、#undef 4、…

深度学习基础-卷积神经网络CNN+深度学习(无代码仅理解)

参考书籍:(找不到资源可以后台私信我) 《深度学习入门:基于Python的理论与实现 (斋藤康毅)》 CNN 概括 其中pooling层有时候会被省略,卷积层的输入输出图像称为特征图(feature map)&#xff0c…