gpt4 book ai didi

Python 变量在生成器中丢失范围?

转载 作者:太空狗 更新时间:2023-10-29 17:19:14 27 4
gpt4 key购买 nike

下面的代码返回 NameError: global name 'self' is not defined。为什么?

lengths = [3, 10]
self.fooDict = getOrderedDict(stuff)

if not all(0 < l < len(self.fooDict) for l in lengths):
raise ValueError("Bad lengths!")

请注意,self.fooDict 是一个包含 35 个条目的 OrderedDict(从集合库中导入)。当我尝试调试时,下面的代码执行时没有错误:

(Pdb) len(self.dataDict)
35
(Pdb) all(0 < size < 35 for size in lengths)
True

但是下面的 debugginf 代码给了我原来的错误:

(Pdb) baz = len(self.dataDict)
(Pdb) all(0 < size < baz for size in lengths)
NameError: global name 'baz' is not defined

最佳答案

简短的回答和解决方法

您遇到了调试器的限制。输入调试器的表达式不能使用非局部范围的值,因为调试器无法创建所需的闭包。

您可以创建一个函数来运行您的生成器,从而同时创建一个新的作用域:

def _test(baz, lengths):
return all(0 < size < baz for size in lengths)

_test(len(self.dataDict), lengths)

请注意,这也适用于集合和字典推导,在 Python 3 中,还适用于列表推导。

长答案,为什么会这样

生成器表达式(以及 set、dict 和 Python 3 列表理解)在新的嵌套命名空间中运行。生成器表达式中的名称 baz 不是该 namespace 中的本地名称,因此 Python 必须在其他地方找到它。 在编译时 Python 确定从何处获取该名称。它将从编译器 可用的范围内进行搜索,如果没有匹配项,则将该名称声明为全局名称。

这里有两个生成器表达式来说明:

def function(some_iterable):
gen1 = (var == spam for var in some_iterable)

ham = 'bar'
gen2 = (var == ham for var in some_iterable)

return gen1, gen2

名称spam 在父作用域中找不到,因此编译器将其标记为全局:

>>> dis.dis(function.__code__.co_consts[1])  # gen1
2 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 17 (to 23)
6 STORE_FAST 1 (var)
9 LOAD_FAST 1 (var)
12 LOAD_GLOBAL 0 (spam)
15 COMPARE_OP 2 (==)
18 YIELD_VALUE
19 POP_TOP
20 JUMP_ABSOLUTE 3
>> 23 LOAD_CONST 0 (None)
26 RETURN_VALUE

索引 12 处的操作码使用 LOAD_GLOBAL 加载 spam 名称。

名称 ham 在函数作用域中找到的,因此编译器生成字节码以从函数中查找作为闭包的名称。 同时名称ham被标记为闭包;为 function 生成的代码对变量的处理方式不同,因此当函数返回时您仍然可以引用它。

>>> dis.dis(function.__code__.co_consts[3])  # gen2
4 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 17 (to 23)
6 STORE_FAST 1 (var)
9 LOAD_FAST 1 (var)
12 LOAD_DEREF 0 (ham)
15 COMPARE_OP 2 (==)
18 YIELD_VALUE
19 POP_TOP
20 JUMP_ABSOLUTE 3
>> 23 LOAD_CONST 0 (None)
26 RETURN_VALUE
>>> function.__code__.co_cellvars # closure cells
('ham',)

名称 ham 加载了 LOAD_DEREF 操作码,函数代码对象将该名称列为闭包。当你反汇编 function 时,你会发现,在其他字节码中:

>>> dis.dis(function)
# ....

4 22 LOAD_CLOSURE 0 (ham)
25 BUILD_TUPLE 1
28 LOAD_CONST 3 (<code object <genexpr> at 0x1074a87b0, file "<stdin>", line 4>)
31 MAKE_CLOSURE 0
34 LOAD_FAST 0 (some_iterable)
37 GET_ITER
38 CALL_FUNCTION 1
41 STORE_FAST 2 (gen2)

# ...

LOAD_CLOSUREMAKE_CLOSURE 字节码为生成器代码对象使用的 ham 创建了一个闭包。

当您在调试器中运行任意表达式时,编译器无法访问您正在调试的命名空间。更重要的是,它不能改变那个命名空间来创建闭包。因此,您只能在生成器表达式中使用globals

关于Python 变量在生成器中丢失范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31298428/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com