Python:退出两个循环

36
  for row in b:
    for drug in drug_input:
      for brand in brand_names[drug]:

我该如何从第三个循环中退出当前循环,并进入下一个值 for row in b:


1
我可能会将这个代码块放在try语句中,并使用raise语句跳出它。 - g.d.d.c
我可以问一下内部循环中什么条件会强制执行“break”吗? - Caleb Hattingh
3
好的。这里有很多答案,它们都是正确的,因为它们确实打破了两个循环。但我认为你写这段代码的方式不对。你为什么要执行这个迭代?回顾一下过去一天的帖子,我认为 b 是一个包含一些药品的 CSV 行列表。为什么你需要针对每行 CSV 中已知的每种药品进行迭代? - Katriel
请扩展您的示例 - 可能有更好的方法来解决它,而无需从外部循环中退出。 - Nas Banov
如果你阅读了链接的页面,就会发现它是作为愚人节玩笑而写的。此外,大多数人认为这样的风格不好且与Python不兼容。 - Joe D
显示剩余3条评论
11个回答

43

下面这个方法使用布尔变量来检查是否完成:

done = False
for x in xs:
    for y in ys:
        if bad:
            done = True
            break

    if done:
        break

如果没有使用break,这个代码块将继续执行。如果已经使用了break,则else语句将被跳过,因此它将看到下一个break。这种方法的优点是不需要使用变量,但有些人可能会觉得更难阅读。

for x in xs:
    for y in ys:
        if bad:
            break
    else:
        continue

    break

好的,它有三个循环,而不是两个。 - Teodor Pripoae
1
嗯,我想也许楼主可以推广到三个循环。 - Donald Miner
2
@Teodor:仔细阅读问题 - 最外层循环不需要退出,因此带有2个循环的示例是正确的。 - Nas Banov
2
好的评论,但是给新手指出如何使用 if...else... 结构可能会更好。 - arivero

8
for row in b:
   more_drugs = True
   for drug in drug_input:
      for brand in brand_names[drug]:
          if something:
              more_drugs = False
              break

      if not more_drugs:
          break

Python没有一种控制结构可以同时从两个循环中跳出,因此您需要手动执行类似于这样的操作。


@cjrh:我很好奇:为什么你会写“好答案”,但不投票支持这个答案呢?没有压力... - Ned Batchelder

8
如果你在一个方法中有三层循环,那么你可能需要重新思考你的设计。
  • 将你的方法拆分成更小、更简单的方法。
  • 使用列表推导式和像 allany 这样的方法来避免编写显式循环。
采取这些措施应该可以解决这个问题。

1
我不确定我同意这一点,特别是在迭代复杂数据结构的情况下。有时你只是有字典中嵌套着字典! - Katriel
@karielalex:你不同意两个观点,还是只有第二个? - Mark Byers
使用“如果您在一个方法中有三个循环级别,那么您可能需要重新考虑设计”的话,我认为在迭代数据结构的情况下存在差异--在这种情况下,多次迭代在语义上是一个部分,不应该被拆分或修剪--而在一个地方做太多事情的情况下则不同。在后一种情况下,我同意您的两点观点。例如,我最近构建了一个具有转换表“初始节点”->“弧类型”->“保护”->“终止节点”的DFA。要遍历所有边缘,您需要三个循环! - Katriel
尽管阅读了这个案例,我认为它很可能是后者的一个例子。for row in b 看起来并不令人鼓舞。 - Katriel
@katrielalex:我同意你的观点,有些算法在单个方法中嵌套三个循环是有意义的,例如 Floyd-Warshall 算法:http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm。但我怀疑这里的情况不是这样的,但从发布的代码中很难判断。我看过一些 OP 的以前的问题,所以我对代码的外观有一个很好的想法,我猜它不太美观,而且得票最高的答案只会让它变得更糟。 - Mark Byers
@Mark:如何使用像all和any这样的方法来避免编写显式循环? - riza

7

在我看来,异常处理比在各处设置变量要好。

for row in b:
    for drug in drug_input:
        try:
            for brand in brand_names[drug]:
                if some_condition:
                    raise StopIteration
        except StopIteration:
            break

1
异常处理仅用于特殊情况! - Caleb Hattingh
1
这是从 Exception 而不是 StandardError 派生的,因为在其正常应用中,这不被认为是一个错误。 - brianz
1
好的,这是一个相当不错的回应 :) 但是说真的,不要为控制流处理异常。在任何语言中都是如此。try-except处理程序是“发生了一些异常情况”的非常普遍的视觉提示。 - Caleb Hattingh
2
如果您要使用异常,为什么不将异常处理提升一级,而不是使用break? - Muhammad Alkarouri
@cjrh - 是的,我大部分时间也同意。通常我不会像这样滥用异常,但在这种情况下,我发现这更加优雅,而且这个特定的异常就是为了停止迭代而设计的。我还会认为这更易读,我们都知道这很重要。 :>) - brianz
我同意@MuhammadAlkarouri的观点:向上提一个级别,并使用pass而不是break会更易读。顺便说一句,这种方法很棒 :) - MestreLion

5
我看到最新的PEP 3136(并被拒绝)请求此功能:http://mail.python.org/pipermail/python-3000/2007-July/008663.html
最接近且最干净的做法是执行以下操作(由于甚至类型名称都有作用域,因此可以在需要的函数内声明LocalBreak):
class LocalBreak(Exception): pass

try:
   for i in ...:
       for h in ...:
           for j in ...:
              if should_break(j):
                 raise LocalBreak()
except LocalBreak:
   pass

5
我建议将两个内部循环放入函数中,并从那里使用return。不过,重新考虑你正在做什么以及如何做可能会给出更好的替代方案。
能否提供当前的伪代码、输入和输出,这样我们就可以尝试消除首次需要中断的情况?我们需要看到循环变量的使用情况,或者更好的是,处理的目标是什么。

3
for a in aa:
     for b in bb:
         for c in cc:
            if c == a[b]:
                break
         else:
             continue
         break

Python支持for...else语句。当内部的break未触发时,将会执行else块。


1
不好意思,您能告诉我这个是为当前内容添加了什么吗? - Alex Gordon

3
如果您嵌套的循环太多,可能需要进行重构。在这种情况下,我认为最好的重构方法是将循环移动到一个函数中并使用return语句。这将强制退出任意数量的循环。例如:
def example(self, drug_input):
    ok = False
    for x in drug_input["names"]:
        for y in range(drug_input["number_of_drugs"]):
            for z in drug_input["list_of_drugs"]:
                # do stuff
                if ok:
                    return drug_costs

也许像这样重新构思逻辑很棘手,但我敢打赌重构会在其他方面有所帮助。


2

未经测试:

inner_termination=False
for row in b:
    for drug in drug_input:
       for brand in brand_names[drug]:
           <blah>
           if break_condition:
               inner_termination=True
               break 
           <blah>
       if inner_termination:
           inner_termination=False
           break 

2
for row in b:
    ok = True
    for drug in drug_input:
        if not ok:
            break;
        for brand in brand_names[drug]:
            if not ok:
                break
            if whatever:
                ok = False

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接