Python:继续外循环中的下一个迭代


135

我想知道是否有任何内置方法可以继续进行python外循环中的下一次迭代。例如,考虑以下代码:

for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            continue
    ...block1...

我希望此继续语句退出jj循环并转到ii循环中的下一项。我可以通过其他方式(通过设置标志变量)来实现此逻辑,但是有没有简单的方法可以做到这一点,或者这就像要求太多吗?


11
实际上,存在一个适用于Python的有效的goto语句:entrian.com/goto。它是作为愚人节的玩笑而发布的:-),但可以正常工作。
codeape

3
哦,请不要用那个goto的玩笑!它非常聪明,但是如果将其放入代码中,您稍后会感到难过。
Ned Batchelder

Answers:


71
for i in ...:
    for j in ...:
        for k in ...:
            if something:
                # continue loop i

在一般情况下,当您有多个循环级别且break对您不起作用时(因为您要继续上一个循环,而不是当前循环的右上循环),可以执行以下操作之一

将您想转义的循环重构为一个函数

def inner():
    for j in ...:
        for k in ...:
            if something:
                return


for i in ...:
    inner()

缺点是您可能需要将以前在范围内的一些变量传递给该新函数。您既可以将它们作为参数传递,也可以在对象上使它们成为实例变量(如果有意义,仅为此函数创建一个新对象),或者全局变量,单例(无论是什么)(ehm,ehm)。

或者,您可以将其定义inner为嵌套函数,然后使其仅捕获所需的内容(可能会更慢?)

for i in ...:
    def inner():
        for j in ...:
            for k in ...:
                if something:
                    return
    inner()

使用例外

从哲学上讲,这就是例外,在必要时,通过结构化的编程构建块(如果是,为时,为时)中断程序流。

这样做的好处是您不必将单个代码分成多个部分。如果这是您在用Python编写代码时正在设计的某种计算,那么这很好。在早期引入抽象可能会使您减速。

这种方法的坏处在于,解释器/编译器作者通常会认为异常是例外情况,因此会相应地对其进行优化。

class ContinueI(Exception):
    pass


continue_i = ContinueI()

for i in ...:
    try:
        for j in ...:
            for k in ...:
                if something:
                    raise continue_i
    except ContinueI:
        continue

为此创建一个特殊的异常类,这样您就不会冒意外地使某些其他异常消失的风险。

完全其他的东西

我相信还有其他解决方案。


不敢相信我没有想到将第二个循环移到另一种方法。我越来越慢
pmccallum

1
对我来说,使用Exceptions是实现此目标的好方法。我同意@ user7610-“从哲学上讲,这是例外情况”。
Renato Byrro

好旧的布尔变量和If语句?
MrR

OP正在寻找一种替代解决方案,“我可以通过其他方式(通过设置标志变量)实现此逻辑[...]”
user7610

149
for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            break
    else:
        ...block1...

Break 将中断内部循环,并且不会执行block1(仅在内部循环正常退出时才会运行)。


1
嗨,还有其他类似的选择吗?因为我想在block1中执行另一个for循环,这样我的代码将深入3级。奇怪的情况。
萨哈斯2009年

3
对我来说,这听起来像是您正在尝试使用for循环来做一些事情,最好以其他方式来实现…
Kimvais

是。这就是为什么我不使用for..else结构的原因。现在,我仍然需要循环,但是我将使用标志变量来转移控制权。
萨哈斯2009年

3
for...else尽管可能会造成混淆,但它通常是一个有用的构造。请记住,else在这种情况下,这意味着“不间断”。
asmeurer 2014年

这似乎仅限于两层循环。我需要更新一些具有三个嵌套循环的代码,而新的客户需求意味着在某些情况下,最内层的循环需要继续进行最外层循环的下一次迭代。我认为您的建议不适用于那种情况。
kasperd '16

42

在其他语言中,您可以标记循环并从标记循环中中断。 Python增强提案(PEP)3136建议将它们添加到Python中,Guido拒绝了它

但是,我拒绝这样的理由是,如此复杂的代码很少需要此功能。在大多数情况下,现有的变通办法可以生成干净的代码,例如使用“返回”。虽然我确信在某些(罕见)实际情况下,代码的清晰性会受到重构的影响,从而可以使用return,但是这被两个问题抵消了:

  1. 复杂性永久地增加了语言。这不仅会影响所有Python实现,而且会影响每个源代码分析工具,当然还会影响该语言的所有文档。

  2. 我期望该功能的滥用程度将超过其正确使用的程度,从而导致代码清晰度净下降(此后对所有编写的Python代码进行衡量)。懒惰的程序员无处不在,在您不了解它之前,您就难以理解难以理解的代码。

因此,如果那是您希望自己没有运气的原因,请查看其他答案之一,因为那里有不错的选择。


4
有趣。我在这里同意Guido。虽然在某些情况下会很好,但是会被滥用。不实现它的另一个原因是,现在可以很直接地在C和Python之间来回移植代码。一旦Python开始使用其他语言所缺乏的功能,这将变得越来越困难。例如,您可以在Python的for循环上使用else语句……这使代码对其他语言的可移植性降低。
eric.frederich

2
我们都向Guido致敬
JnBrymn'3

4
这比起良好的反论点更像是一个红色的继承人,但是在我看来for-else,与命名循环相比,它的行为更复杂,更难以阅读,并且可能更易滥用(如果不是彻底的错误)。我想我会使用一个else与之不同的关键字-也许类似的东西resume会很好?您break处于循环中,并且resume紧随其后吗?
ArtOfWarfare 2014年

5
这让我很难过。我不敢相信我同时爱又恨Python。如此美丽,却如此。
jlh

5
@jlh对我来说主要是wtf。有时我认为它不是出于合法目的而是为了与众不同。这是一个很好的例子。我经常遇到打破外部循环的需要。
Rikaelus

14

我认为您可以执行以下操作:

for ii in range(200):
    restart = False
    for jj in range(200, 400):
        ...block0...
        if something:
            restart = True
            break
    if restart:
        continue
    ...block1...

4
-1:OP明确表示他们知道他们可以做这样的事情,而这看起来像是已接受答案的混乱版本(比您的答案早8个月,因此不可能不是您只是错过了已接受的答案)回答)。
ArtOfWarfare 2014年

10
如果您以前从未看过for,那么可以接受的答案就不清楚else(而且我认为,即使是大多数不记得自己头脑的人,它的工作原理也是如此)。
asmeurer 2014年

3

我认为最简单的方法之一是用“ break”语句代替“ continue”,即

for ii in range(200):
 for jj in range(200, 400):
    ...block0...
    if something:
        break
 ...block1...       

例如,以下是简单的代码,可查看其运行的确切情况:

for i in range(10):
    print("doing outer loop")
    print("i=",i)
    for p in range(10):
        print("doing inner loop")
        print("p=",p)
        if p==3:
            print("breaking from inner loop")
            break
    print("doing some code in outer loop")

2

解决此类问题的另一种方法是使用Exception()。

for ii in range(200):
    try:
        for jj in range(200, 400):
            ...block0...
            if something:
                raise Exception()
    except Exception:
        continue
    ...block1...

例如:

for n in range(1,4):
    for m in range(1,4):
        print n,'-',m

结果:

    1-1
    1-2
    1-3
    2-1
    2-2
    2-3
    3-1
    3-2
    3-3

假设如果m = 3,我们想从m循环跳转到外部n循环:

for n in range(1,4):
    try:
        for m in range(1,4):
            if m == 3:
                raise Exception()            
            print n,'-',m
    except Exception:
        continue

结果:

    1-1
    1-2
    2-1
    2-2
    3-1
    3-2

参考链接:http : //www.programming-idioms.org/idiom/42/continue-outer-loop/1264/python


1

我们想找到一些东西,然后停止内部迭代。我使用标志系统。

for l in f:
    flag = True
    for e in r:
        if flag==False:continue
        if somecondition:
            do_something()
            flag=False

我不知道为什么您的解决方案被否决了;有人发布了完全相同的内容并被投票10次
Locane

我对stackoverflow不是很幸运。
以斯帖

1
也许是因为这里已经发布了几乎完全相同的东西,所以我想...而那False:continue是...异常的格式。正如在以指数为标准的“自然”系统中经常发生的情况一样,您只需在SO上获得几次幸运就可以积累大量的信誉点。无论如何,我的“最佳”答案通常是最受欢迎的答案。
user7610

0

我只是做了这样的事情。我对此的解决方案是用列表理解替换内部for循环。

for ii in range(200):
    done = any([op(ii, jj) for jj in range(200, 400)])
    ...block0...
    if done:
        continue
    ...block1...

其中op是作用于ii和jj组合的布尔运算符。就我而言,如果任何操作返回true,我就完成了。

这实际上与将代码分解为一个函数没有什么不同,但是我认为使用“ any”运算符对布尔值列表进行逻辑或,然后全部执行逻辑是很有趣的。它还避免了函数调用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.