我正在做一些图遍历。在每个点上,我保存了一个可以探索的其他可能选项的生成器。后来,我探索了一些这些生成器,但它不起作用。
这是一个简化示例,您可以在其中看到所有生成器中的“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]
"""
答案 0 :(得分: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))
由于在创建genexp时仅对环部分进行一次求值,因此在genexp范围内修复了node
的单个值。
如果这种方式太难看了你,你将不得不编写一个显式的生成器函数而不是使用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))
这里生成器关闭名称nodeVal
,但由于每个生成器都是由一个单独的函数调用创建的,因此每个生成器都有自己的nodeVal
值,一切都很好。
答案 1 :(得分: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))