结合无限与有限迭代器
combine infinite with finite iterator
我有两个嵌套循环,外部循环使用无限生成器,内部循环使用有限迭代器(数字列表):
for j in itertools.count():
for q in Q:
...
在给定条件下,我需要从两个循环中跳出 break
,这使得代码不够优雅(我必须设置一个标志来触发第二个 break
)。为了避免这种情况,我想我会使用
将两个循环合二为一
for j, q in itertools.product(itertools.count(), Q):
...
但是当我这样做时,我的计算机变得越来越慢,显然是在交换内存,直到我不得不终止 Python 进程。
product
的文档说
the actual implementation does not build up intermediate results in memory
所以我假设 product
的结果是一个生成器,它根据需要创建产品的元素。但是减速和交换与此相反。
我做错了什么?我怎样才能实现我想要的,将无限生成器与列表组合在一起的单个循环?
您可以使用推导式创建生成器。 Comprehensions 支持使用多个 for
s
一次迭代多个可迭代对象
for j, q in ((j, q) for j in itertools.count() for q in Q):
...
您的文档引用来自 the following context:
This function is roughly equivalent to the following code, except that
the actual implementation does not build up intermediate results in
memory:
def product(*args, repeat=1):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools = [tuple(pool) for pool in args] * repeat
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
中间结果都是result
个列表。实际的 itertools.product
实现不需要构建这些。但是,它确实构建了类似于 pools
.
的东西
itertools.product
需要能够重复迭代其输入,因此它将所有输入具体化为元组。当 repeat
为 1
时,此要求不适用于第一个输入,但实施无论如何都会具体化所有输入,可能是为了保持一致性或易于实施。 itertools.count()
无法完全实现,因此您看到的结果。
您有一个选择是生成器表达式。以下循环:
for j, q in ((j, q) for j in itertools.count() for q in Q):
会像您希望 itertools.product
那样表现,尽管它会比普通嵌套循环慢(因为生成器的开销)。性能损失只是一个常数因素,它不会减慢循环体的速度,所以很少有问题。
如果你可以将嵌套循环分解成一个函数,你也可以使用 return
而不是 break
:
def helper_function(stuff):
for j in itertools.count():
for q in Q:
...
if whatever:
return
...
添加以防对任何人都有用,并且基于,可以使用异常来中断或继续外循环,例如:
class ContinueOuter(Exception): pass
class BreakOuter(Exception): pass
try:
for i in range(10): # loop we want to break or continue from
try:
for j in range(3):
print(i, j)
if (i, j) == (3, 1):
raise ContinueOuter
elif (i, j) == (4, 0):
raise BreakOuter
except ContinueOuter:
pass
except BreakOuter:
pass
我有两个嵌套循环,外部循环使用无限生成器,内部循环使用有限迭代器(数字列表):
for j in itertools.count():
for q in Q:
...
在给定条件下,我需要从两个循环中跳出 break
,这使得代码不够优雅(我必须设置一个标志来触发第二个 break
)。为了避免这种情况,我想我会使用
for j, q in itertools.product(itertools.count(), Q):
...
但是当我这样做时,我的计算机变得越来越慢,显然是在交换内存,直到我不得不终止 Python 进程。
product
的文档说
the actual implementation does not build up intermediate results in memory
所以我假设 product
的结果是一个生成器,它根据需要创建产品的元素。但是减速和交换与此相反。
我做错了什么?我怎样才能实现我想要的,将无限生成器与列表组合在一起的单个循环?
您可以使用推导式创建生成器。 Comprehensions 支持使用多个 for
s
for j, q in ((j, q) for j in itertools.count() for q in Q):
...
您的文档引用来自 the following context:
This function is roughly equivalent to the following code, except that the actual implementation does not build up intermediate results in memory:
def product(*args, repeat=1): # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 pools = [tuple(pool) for pool in args] * repeat result = [[]] for pool in pools: result = [x+[y] for x in result for y in pool] for prod in result: yield tuple(prod)
中间结果都是result
个列表。实际的 itertools.product
实现不需要构建这些。但是,它确实构建了类似于 pools
.
itertools.product
需要能够重复迭代其输入,因此它将所有输入具体化为元组。当 repeat
为 1
时,此要求不适用于第一个输入,但实施无论如何都会具体化所有输入,可能是为了保持一致性或易于实施。 itertools.count()
无法完全实现,因此您看到的结果。
您有一个选择是生成器表达式。以下循环:
for j, q in ((j, q) for j in itertools.count() for q in Q):
会像您希望 itertools.product
那样表现,尽管它会比普通嵌套循环慢(因为生成器的开销)。性能损失只是一个常数因素,它不会减慢循环体的速度,所以很少有问题。
如果你可以将嵌套循环分解成一个函数,你也可以使用 return
而不是 break
:
def helper_function(stuff):
for j in itertools.count():
for q in Q:
...
if whatever:
return
...
添加以防对任何人都有用,并且基于,可以使用异常来中断或继续外循环,例如:
class ContinueOuter(Exception): pass
class BreakOuter(Exception): pass
try:
for i in range(10): # loop we want to break or continue from
try:
for j in range(3):
print(i, j)
if (i, j) == (3, 1):
raise ContinueOuter
elif (i, j) == (4, 0):
raise BreakOuter
except ContinueOuter:
pass
except BreakOuter:
pass