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

209 投票
9 回答
213910 浏览
提问于 2025-04-15 16:45

我想知道在Python中有没有内置的方法可以让外层循环继续到下一次迭代。比如,看看下面这段代码:

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

我希望这个continue语句能让jj循环结束,然后直接跳到ii循环的下一个项目。我可以通过设置一个标志变量来实现这个逻辑,但有没有更简单的方法呢?还是说这要求太高了?

9 个回答

62

在其他编程语言中,你可以给循环加个标签,然后从这个标签的循环中跳出来。Python的一个提案(PEP 3136)曾建议在Python中加入这个功能,但Guido拒绝了这个提案

他表示,拒绝这个提案的原因是,代码复杂到需要这个功能的情况非常少见。在大多数情况下,有现成的解决办法可以写出更简洁的代码,比如使用'return'。虽然我相信确实有一些(少见的)真实案例,重构代码后会影响代码的清晰度,但这可以通过两个问题来抵消:

  1. 这个功能会给语言增加复杂性,且是永久性的。这不仅影响所有Python的实现,还会影响每一个源代码分析工具,当然也包括所有的语言文档。

  2. 我预计这个功能会被滥用的情况会比正确使用的情况多,导致代码的清晰度整体下降(在今后所有写的Python代码中衡量)。懒惰的程序员随处可见,没过多久,你就会发现手头的代码变得一团糟,根本看不懂。

所以如果你希望有这个功能,那你可能要失望了,不过可以看看其他答案,那里面有不错的选择。

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

Break 这个指令会让里面的循环停止,也就是说,block1 这部分代码不会被执行(只有当里面的循环正常结束时,block1 才会运行)。

125
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()

这样做的缺点是,你可能需要把一些之前在作用域内的变量传递给这个新函数。你可以选择把它们作为参数传递,或者把它们变成对象的实例变量(如果有必要,可以为这个函数创建一个新对象),或者使用全局变量、单例模式等等(咳咳)。

另外,你也可以把 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

为此创建一个特殊的异常类,这样你就不会意外地抑制其他异常。

完全不同的其他方法

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

撰写回答