3

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

这是一个简化的示例,您可以看到所有生成器中的“node”变量都设置为 3。(所以生成器指向“节点”变量,但“节点”变量在生成器被消耗之前发生了变化。

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

有没有简单的方法来做到这一点?

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]
"""
4

2 回答 2

6

您的生成器表达式引用全局变量node。由于这是 genexp 中的一个自由变量,它关闭了name,而不是 value。每次从生成器中获取项目时,都会使用 的当前1 + node_size[node]+j评估表达式。也就是说,每次生成器高级时都会读取 的值,而不是在创建它时一劳永逸地读取。当您开始从生成器中获取项目时,为 3,因此生成器中的所有项目都反映了该值。nodenodenode

要得到你想要的,你需要node在生成器函数本身中进行绑定。一种快速的方法是强制node进入 genexp 的循环部分:

it = (1 + node_size[node]+j for node in [node] for j in xrange(3))

由于在创建 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))

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

于 2013-04-11T03:48:54.240 回答
1

如何制作关闭节点大小值的生成器函数?

node_size = {1:1, 2:2, 3:1, 4:3}

iters = []
for node in xrange(1, 4):
    def it(n=node_size[node]):
        for j in xrange(1, 4):
            yield n + j
    itr = it()
    iters.append(itr)

for iter_ in iters:
    print list(iter_)

这为我打印了正确的结果。

编辑:@BrenBarn 发布了一个答案,让我直接得到了这个答案:

node_size = {1:1, 2:2, 3:1, 4:3}

iters = []
for node in range(1, 4):
    n = node_size[node]
    itr = xrange(n+1, n+4)
    iters.append(itr)

for iter_ in iters:
    print list(iter_)

当你调用xrange()它时,它会评估它的参数,然后它会给你一个迭代器来产生数字。

我认为在 Python 中没有更有效的方法可以做到这一点!

在这种情况下,我们能够避免所有的数学运算并xrange()准确地得出所需的数字。如果您确实需要评估表达式,您仍然可以使用生成器表达式方式:

itr = (1+j for j in xrange(n, n+3))
于 2013-04-11T03:47:01.433 回答