关于python:打破嵌套循环

Breaking out of nested loops

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
How to break out of multiple loops in Python?

有没有比抛出异常更容易脱离嵌套循环的方法?(在Perl中,您可以为每个循环提供标签,并且至少继续一个外部循环。)

1
2
3
4
5
for x in range(10):
    for y in range(10):
        print x*y
        if x*y > 50:
           "break both loops"

也就是说,有没有比以下更好的方法:

1
2
3
4
5
6
7
8
9
10
class BreakIt(Exception): pass

try:
    for x in range(10):
        for y in range(10):
            print x*y
            if x*y > 50:
                raise BreakIt
except BreakIt:
    pass


1
2
3
4
5
6
7
8
for x in xrange(10):
    for y in xrange(10):
        print x*y
        if x*y > 50:
            break
    else:
        continue  # only executed if the inner loop did NOT break
    break  # only executed if the inner loop DID break

同样适用于更深的回路:

1
2
3
4
5
6
7
8
9
10
11
12
for x in xrange(10):
    for y in xrange(10):
        for z in xrange(10):
            print x,y,z
            if x*y*z == 30:
                break
        else:
            continue
        break
    else:
        continue
    break


它至少被建议过,但也被拒绝了。我认为除了重复测试或重新组织代码之外,没有其他方法。有时候有点烦人。

在拒绝信中,范罗森提到使用return,这是非常明智的,我需要亲自记住。:)


如果能够将循环代码提取到函数中,那么可以随时使用return语句退出最外层的循环。

1
2
3
4
5
6
7
def foo():
    for x in range(10):
        for y in range(10):
            print x*y
            if x*y > 50:
                return
foo()

如果很难提取该函数,则可以使用内部函数,如@bjd2385所建议的。

1
2
3
4
5
6
7
8
9
10
def your_outer_func():
    ...
    def inner_func():
        for x in range(10):
            for y in range(10):
                print x*y
                if x*y > 50:
                    return
    inner_func()
    ...


使用itertools.product!

1
2
3
4
from itertools import product
for x, y in product(range(10), range(10)):
    #do whatever you want
    break

下面是python文档中指向itertools.product的链接:http://docs.python.org/library/itertools.html itertools.product

您还可以循环一个数组理解,其中包含2个fors,并在需要时中断。

1
2
3
4
5
>>> [(x, y) for y in ['y1', 'y2'] for x in ['x1', 'x2']]
[
    ('x1', 'y1'), ('x2', 'y1'),
    ('x1', 'y2'), ('x2', 'y2')
]

(为清晰起见格式化)


有时我使用布尔变量。天真,如果你愿意的话,但我觉得阅读起来相当灵活和舒适。测试一个变量可以避免再次测试复杂的条件,也可以从内部循环的几个测试中收集结果。

1
2
3
4
5
6
7
8
    x_loop_must_break = False
    for x in range(10):
        for y in range(10):
            print x*y
            if x*y > 50:
                x_loop_must_break = True
                break
        if x_loop_must_break: break

如果要引发异常,可能会引发StopIteration异常。这至少会使意图变得明显。


还可以重构代码以使用生成器。但这可能不是所有类型的嵌套循环的解决方案。


在这种特殊情况下,可以使用itertools.product将循环与现代的python(3.0,也可能是2.6)合并。

我个人认为这是一个经验法则,如果你嵌套了太多的循环(如中,多于2个),你通常能够将其中一个循环提取到不同的方法中,或者将循环合并到一个方法中,就像在本例中那样。