将生成器附加到循环中的堆栈,生成器指向最终循环变量

rob*_*ing 3 python scope generator pass-by-reference

我正在做一些图遍历.在每个点上,我保存了一个可以探索的其他可能选项的生成器.后来,我探索了一些这些生成器,但它不起作用.

这是一个简化的示例,您可以在其中看到所有生成器中的"node"变量都设置为3.(因此生成器指向"node"变量,但"node"变量在生成器消耗之前发生变化.

在我的特定情况下,我可以存储一些指针并添加如何处理这些指针以重新创建生成器的逻辑 - 但这是一个丑陋的解决方案.

有一个简单的方法吗?

node_size = {1:1, 2:2, 3:1, 4:3}
iters = []
for node in range(1,4):
    it = (1 + node_size[node]+j for j in xrange(3))
    #it = iter(list(it)) #remove comment to get correct result but very slow.
    iters.append(it)

for iter_ in iters:
    print list(iter_)

"""
Correct Output
[2, 3, 4]
[3, 4, 5]
[2, 3, 4]
"""

"""
Actual Output:
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
"""
Run Code Online (Sandbox Code Playgroud)

Bre*_*arn 6

您的生成器表达式引用全局变量node.由于这是genexp中的一个自由变量,因此它会关闭名称,而不是值.每次从生成器中获取项目时,将1 + node_size[node]+j使用当前值来计算表达式node.也就是说,node每次生成器前进时都会读取值,而不是在创建它时一次又一次读取.当您开始从生成器中抓取项目时,node为3,因此生成器中的所有项目都反映该值.

为了得到你想要的东西,你需要绑定node生成器函数本身.一个快速的方法是强制node进入genexp的循环部分:

it = (1 + node_size[node]+j for node in [node] for j in xrange(3))
Run Code Online (Sandbox Code Playgroud)

由于在创建genexp时仅对环部分进行一次求值,因此这将修复nodegenexp范围内的单个值.

如果这种方式太难看了你,你将不得不编写一个显式的生成器函数而不是使用genexp:

def gen(nodeVal):
    for j in xrange(3):
        yield 1 + node_size[nodeVal]+j
for node in range(1, 4):
    iters.append(gen(node))
Run Code Online (Sandbox Code Playgroud)

这里生成器关闭名称nodeVal,但由于每个生成器都是由一个单独的函数调用创建的,因此每个生成器都有自己的值,nodeVal并且一切都很好.