Python:在外循环中继续下一次迭代

Posted

技术标签:

【中文标题】Python:在外循环中继续下一次迭代【英文标题】:Python: Continuing to next iteration in outer loop 【发布时间】:2010-12-23 23:07:55 【问题描述】:

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

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

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

【问题讨论】:

实际上存在一个适用于 Python 的 goto 语句:entrian.com/goto。它是作为愚人节的玩笑发布的:-),但应该可以工作。 哦,请不要用那个 goto 玩笑!它非常聪明,但如果你把它放到你的代码中,你以后会很难过。 【参考方案1】:
for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            break
    else:
        ...block1...

Break会打断内循环,block1不会被执行(只有内循环正常退出才会运行)。

【讨论】:

您好,还有其他类似的选择吗?因为我想在 block1 中再做一个 for 循环,这样我的代码就会深入 3 层。奇怪的情况。 对我来说,这听起来像是你正在尝试用 for 循环做一些事情,最好以不同的方式处理...... 是的。这就是为什么我没有使用 for..else 结构。现在我仍然需要 for 循环,但我将使用标志变量来转移控制。 for...else 通常是一个有用的结构,尽管它可能会令人困惑。请记住,else 在这种情况下意味着“不间断”。 这似乎仅限于两层循环。我需要更新一些具有三个嵌套循环的代码,并且新的客户要求意味着在某些情况下最内层循环需要继续最外层循环的下一次迭代。我认为您的建议不适用于那种情况。【参考方案2】:

在其他语言中,您可以标记循环并从标记的循环中中断。 Python Enhancement Proposal (PEP) 3136 suggested adding these to Python 但Guido rejected it:

但是,我拒绝了它,因为代码太复杂了 需要这个功能是非常少见的。在大多数情况下,存在 产生干净代码的变通方法,例如使用'return'。 虽然我确信有一些(罕见的)真实案例可以清楚地说明 代码将遭受重构,使其可以使用 返回,这被两个问题所抵消:

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

    我预计该功能将被滥用 使用正确,导致代码清晰度净下降(在 此后编写的所有 Python 代码)。懒惰的程序员无处不在, 在不知不觉中,你的手上已经出现了令人难以置信的混乱 难以理解的代码。

因此,如果这就是您所希望的,那么您就不走运了,但请查看其他答案之一,因为那里有不错的选择。

【讨论】:

有趣。我同意 Guido 的观点。虽然在某些情况下会很好,但它会被滥用。不实现它的另一个原因是,现在在 C 和 Python 之间来回移植代码非常简单。一旦 Python 开始使用其他语言缺乏的特性,这将变得更加困难。例如,您可以在 Python 中的 for 循环上使用 else 语句......这使得代码对其他语言的可移植性降低。 向我们的 BDFL 致敬 Guido 这与其说是一个好的反论点,不如说是一个红色继承人,但在我看来,for-else 的行为更复杂,更难以阅读,并且可能更被滥用(如果不是一个彻底的错误)而不是命名循环。我想我会使用与else 不同的关键字——也许像resume 这样的关键字会很好?你break 在循环中,resume 紧随其后? 这让我很难过。我无法相信我是如何同时又爱又恨 Python。如此美丽,但如此wtf。 @jlh 对我来说主要是 wtf。有时我认为它想要与众不同不是为了合法目的,而只是为了与众不同。这是一个很好的例子。我经常遇到打破外部循环的需要。【参考方案3】:

我认为你可以这样做:

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

【讨论】:

-1:OP 明确表示他们知道他们可以做这样的事情,而这看起来像是已接受答案的混乱版本(比你的早 8 个月,所以它不能一直是你错过了接受的答案)。 如果您以前从未见过forelse,那么接受的答案就不是很清楚了(而且我认为即使是大多数人都记不起它是如何工作的)。【参考方案4】:
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()

使用例外

从哲学上讲,这就是例外的用途,在必要时通过结构化编程构建块(if、for、while)打破程序流程。

优点是您不必将单段代码分成多个部分。如果它是您在用 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

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

完全不同的东西

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

【讨论】:

不敢相信我没有想到将我的第二个循环移动到另一种方法。我变慢了 对我来说,使用异常是实现这一目标的好方法。我同意@user7610 - “从哲学上讲,这就是例外”。 好的旧布尔变量和 If 语句? 这里设置一个flag很正常,虽然函数解决方案看起来更清晰。 @kontur StopIteration 旨在用于 Python 迭代器(以及相关构造:生成器和协程)。在 IMO 中重复使用它是不合适的。【参考方案5】:

处理此类问题的另一种方法是使用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

【讨论】:

【参考方案6】:

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

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

【讨论】:

我不知道您的解决方案为何被否决;有人发布了基本完全相同的内容并获得了 10 次投票 我对***不是很幸运。 也许是因为这里已经发布了基本完全相同的内容,我猜...而False:continue 的事情是...不寻常的格式。正如在以指数为标准的“自然”系统中经常出现的情况一样,您只需在 SO 上幸运几次即可积累大量声誉点。无论如何,我的“最佳”答案通常是最不受欢迎的。 @Esther 几个指针:1)不要使用flag == False;应该是flag is False 来测试与TrueFalse 是否相等。此外,在 if 语句中,您不需要像这样测试相等性。就说if not flag:【参考方案7】:

我刚刚做了这样的事情。我的解决方案是用列表理解替换内部 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”运算符对一个布尔值列表执行逻辑 OR 并在一行中执行所有逻辑很有趣。它也避免了函数调用。

【讨论】:

【参考方案8】:

我认为实现这一目标的最简单方法之一是将“继续”替换为“中断”语句,即

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")

【讨论】:

【参考方案9】:

我知道继续外循环的最佳方法是使用范围在外循环下的布尔值并打破内循环。虽然,根据用例,您可能不会中断内循环,但在其内循环内继续外循环隐含地暗示您希望立即跳转到外循环的第一行并避免在内循环中进一步执行。这就是我添加break 声明的原因。

for i in range(0, 6):
    should_continue = False

    for f in range(1, i * 2):
        print(f"f = f")
        if (not (f % 1337) or not (f % 7)):
            print(f"f can be divided, continue outer loop")
            should_continue = True
            # leaves inner loop
            break

    if(should_continue): continue
    # Outer loop's code goes here
    print(f'Reached outer loop\ni = i')

这种方法避免调用任何函数并处理可能的缺点。众所周知,调用函数是一项相当昂贵的操作,特别是对于游戏。现在想象一个深度嵌套的 for 循环将运行数百万次,将其包装在函数中不会带来流畅的体验。

将循环包装在异常块中也是一个坏主意,并且会比函数慢得多。这是因为 Python 需要大量开销来触发异常机制并在稍后恢复运行时状态,异常被设计为异常使用。考虑到这一点,即使是一些 CPU 优化(例如 speculative execution)也不应该应用于异常块,而且可能不会。

我在这种方法中发现的唯一“问题”是break 将跳出内部循环一次,然后落在continue 上,而continue 又会再跳一次。与 C 或 javascript 中的 goto 语句相比,这有点笨拙,但对性能没有任何明显的影响,因为它只会生成一条额外的指令,该指令的运行速度与您的 CPU 时钟或解释器一样快实施。

【讨论】:

以上是关于Python:在外循环中继续下一次迭代的主要内容,如果未能解决你的问题,请参考以下文章

如果满足条件,则移动到嵌套 for 循环中的下一次迭代

Python入门教程第20篇 continue语句

在一定时间后继续循环

循环的使用

相当于 Ruby 中的“继续”

如何在循环中回滚到下一次迭代?