文章目录
- 参考
- 描述
- globals() 函数
- For Loop 过程中产生的迭代变量
- Runtime Error
- dictionary changed size during iteration
- 异常产生原因
- 解决方案
- copy 方法
- 绕过 RuntimeError
- 产生 RuntimeError 异常的基本要求
- 遍历 locals() 时可能产生的 RuntimeError
参考
项目 | 描述 |
---|---|
Python 官方文档 | https://docs.python.org/zh-cn/3/ |
搜索引擎 | Google 、Bing |
描述
项目 | 描述 |
---|---|
PyCharm | 2023.1 (Professional Edition) |
Python | 3.10.6 |
globals() 函数
在 Python 中,globals()
是一个 内置函数
,它返回一个 表示当前模块全局命名空间的字典
,该字典存储了当前模块中定义的所有全局变量和函数。
globals()
函数返回一个字典,其中包含全局作用域中的变量名及其对应的值。这个字典可以用于 访问、修改和删除
全局变量。请注意,通过globals()
函数返回的字典是 实时更新
的,反映了当前的全局命名空间的状态。
举个栗子
free_var = 'Hello World'# globals() 在程序的任何位置访问的结果都是
# 一致的。
for name in globals().copy():print(f'{name}\t{globals()[name]}')# 在调用 globals() 函数时,MyClass
# 与 func 都尚未添加到全局命名空间中。
# 仅但解释器执行到 MyClass 与 func 的定义部分
# Python 才会将其添加至全局命名空间。
class MyClass:passprint()
print('MyClass' in globals())
print('func' in globals())
print()def func():passprint('MyClass' in globals())
print('func' in globals())
执行效果
当解释器加载一个 模块(任何 Python 文件都可被视为模块)
时,它首先会创建一个新的全局作用域对象,用于存储该模块的全局变量、函数、类和其他定义。解释器会 按顺序解析
模块中的代码,并在执行过程中 将标识符绑定到相应的作用域对象中
。
__name__ __main__
__doc__ None
__package__ None
__loader__ <_frozen_importlib_external.SourceFileLoader object at 0x000002868F414820>
__spec__ None
__annotations__ {}
__builtins__ <module 'builtins' (built-in)>
__file__ C:\Users\RedHeart\PycharmProjects\pythonProject\example.py
__cached__ None
free_var Hello WorldTrue
FalseTrue
True
For Loop 过程中产生的迭代变量
在 Python 的 for 循环
中,迭代变量
是指在每次迭代中 用于表示可迭代对象中的当前元素的变量
。迭代变量的名称是根据程序员的选择来确定的,它可以是 任意有效的变量名
。
举个栗子
# 在下面的 For Loop 中,迭代变量为 num
for num in range(100):pass# 在 For Loop 过程中,将会在当前作用域下
# 创建迭代变量。因此在 For Loop 结束后,
# 仍能够在当前作用域中访问到该变量。
print(num)
执行效果
99
Runtime Error
dictionary changed size during iteration
当你在 Python 中尝试直接遍历 globals()
的返回结果时,不可避免的将引发 Runtime Error
。对此,请参考如下示例:
for key in globals():pass
执行效果
在 Python 中执行上述代码后,Python 将抛出异常错误,其中包含如下内容:
RuntimeError: dictionary changed size during iteration
异常产生原因
字典在内部使用 散列表(也被称为哈希表)
来实现 键值对
的 存储
和 查找
。散列表的 工作原理
涉及将键通过哈希函数转换为索引,然后在存储桶中存取相应的值。
散列表的结构在 迭代过程
中是 敏感的
。当你在迭代字典的 视图对象(字典提供的视图对象是动态的、实时反映字典内容变化的可迭代对象)
时,若 添加
或 删除
字典中的条目,会导致 字典的结构发生变化
。
当你迭代字典提供的视图对象时,迭代器会维护一个 内部状态
来跟踪字典的条目。如果在迭代过程中修改字典的大小,迭代器的内部状态与字典的实际状态将不再一致
。如果继续迭代,可能会产生意想不到的结果或错误的行为。为了保持迭代的一致性和正确性,Python 选择在这种情况下抛出 RuntimeError
异常,以提醒开发者字典发生了变化,并防止继续迭代可能导致的问题。
在迭代 globals()
函数时,不可避免的将引发 Runtime Error
。其原因是,在迭代过程中产生的迭代变量将注册至当前作用域中,导致字典的结构发生改变,从而引发了 RuntimeError
异常错误。
在迭代 globals()
函数时,也可以存在特例不会导致发生 RuntimeError
。对此,请参考如下示例:
def func():for i in globals():passfunc()
在上述代码中,迭代变量将注册至 局部作用域
中,代表全局命名空间的字典并不会因此受到影响。因此,Python 将正常执行上述代码而不引发异常错误。
解决方案
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
在迭代过程中产生的迭代变量将注册至当前作用域的命名空间中。若在全局作用域中迭代 globals()
函数,将在全局命名空间中注册迭代变量,这会导致 globals()
函数发生改变,从而引发 RuntimeError
。若我们迭代的是 globals()
的副本,即使迭代过程中产生的迭代变量会导致原对象发生改变,但这并不会影响到其相关的拷贝对象。
举个栗子
for i in globals().copy():pass
执行效果
遍历对象由 globals()
改为 globals().copy()
后,执行上述代码,Python 将不会抛出 RuntimeError
异常错误。
产生 RuntimeError 异常的基本要求
在遍历 globals()
产生 RuntimeError
的一个基本要求是,globals()
函数的返回结果中存在 至少一个
的键值对。第一次迭代过程中将产生并注册 迭代变量(Python 将从被迭代对象中取出一个值并赋值给迭代变量)
,若没有进行第二次迭代,则 For Loop
将无法发现被迭代字典对象的改变。
由于 Python 在执行代码前会创建多个全局变量供用户使用,因此 globals()
的返回值总是会满足该要求。但由 遍历 globals() 产生 RuntimeError 的基本要求
我们可以理解为什么遍历 globals()
时,总是在访问第一个 globals()
元素后产生 RuntimeError
。对此,请参考如下示例:
for i in globals():print(i)
执行效果
执行上述代码后,Python 将在输出 globals()
返回的字典对象的第一个 键
后产生 RuntimeError
。
__name__
遍历 locals() 时可能产生的 RuntimeError
在 Python 中,locals()
是一个 内置函数
,用于返回 当前作用域的命名空间的字典
,该字典存储了当前作用域中定义的所有全局变量和函数。
locals()
函数返回一个字典,其中包含局部作用域中的变量名及其对应的值。这个字典可以用于 访问、修改和删除
当前作用域中的变量。
locals()
与 globals()
函数的功能类似,这也意味着 locals()
也存在与 globals()
相同的问题,但 locals()
相比 globals()
来说,更为复杂也更难被触发。如需对其进行了解,欢迎前往我的另一篇博客 遍历 locals() 时可能产生的 RuntimeError。