以下python代码生成[(0,0),(0,7)...(0,693)]而不是预期的元组列表,它组合了3的倍数和7的倍数:
multiples_of_3 = (i*3 for i in range(100))
multiples_of_7 = (i*7 for i in range(100))
list((i,j) for i in multiples_of_3 for j in multiples_of_7)
此代码修复了问题:
list((i,j) for i in (i*3 for i in range(100)) for j in (i*7 for i in range(100)))
问题:
答案 0 :(得分:3)
生成器对象是迭代器,因此是一次性的。它不是 iterable ,它可以生成任意数量的独立迭代器。这种行为不是你可以用某个开关改变的,所以任何解决方法相当于使用可迭代(例如列表)而不是生成器或重复构建生成器。
第二个片段执行后者。根据定义,它等同于循环
for i in (i*3 for i in range(100)):
for j in (i*7 for i in range(100)):
...
希望在这里,在外循环的每次迭代中重新评估后一个生成器表达式并不奇怪。
答案 1 :(得分:2)
正如您所发现的,生成器表达式创建的对象是一个迭代器(更准确地说是一个 generator-iterator ),设计为只消耗一次。如果你需要一个可重置的生成器,只需创建一个真正的生成器并在循环中使用它:
def multiples_of_3(): # generator
for i in range(100):
yield i * 3
def multiples_of_7(): # generator
for i in range(100):
yield i * 7
list((i,j) for i in multiples_of_3() for j in multiples_of_7())
你的第二个代码是有效的,因为在外循环的每次传递中都会计算内循环((i*7 ...)
)的表达式列表。这导致每次都创建一个新的生成器迭代器,它为您提供所需的行为,但代价是代码清晰。
要了解发生了什么,请记住,当for
循环迭代迭代器时,迭代器没有“重置”。 (这是一个特性;这样的重置会破坏遍历大型迭代器的碎片,对于生成器来说是不可能的。)例如:
multiples_of_2 = iter(xrange(0, 100, 2)) # iterator
for i in multiples_of_2:
print i
# prints nothing because the iterator is spent
for i in multiples_of_2:
print i
......而不是:
multiples_of_2 = xrange(0, 100, 2) # iterable sequence, converted to iterator
for i in multiples_of_2:
print i
# prints again because a new iterator gets created
for i in multiples_of_2:
print i
生成器表达式等同于被调用的生成器,因此只能迭代一次。
答案 2 :(得分:1)
如果要将生成器表达式转换为多遍迭代,则可以以相当常规的方式完成。例如:
class MultiPass(object):
def __init__(self, initfunc):
self.initfunc = initfunc
def __iter__(self):
return self.initfunc()
multiples_of_3 = MultiPass(lambda: (i*3 for i in range(20)))
multiples_of_7 = MultiPass(lambda: (i*7 for i in range(20)))
print list((i,j) for i in multiples_of_3 for j in multiples_of_7)
从定义事物的角度来看,输入的工作量相似:
def multiples_of_3():
return (i*3 for i in range(20))
但是从用户的角度来看,他们写multiples_of_3
而不是multiples_of_3()
,这意味着对象multiples_of_3
与任何其他可迭代对象都是多态的,例如{{1 }或tuple
。
输入list
的需要有点不雅,是真的。我不认为在语言中引入“可迭代的理解”会给你提供你想要的东西,同时保持向后兼容性。但是只有那么多标点字符,我怀疑这会被认为是值得的。
答案 3 :(得分:0)
我发现的真正问题是关于单通道和多通道迭代,以及目前没有标准机制来确定可迭代的单通道还是多通道:见Single- vs. Multi-pass iterability