给定以下代码(不起作用):
while True:
# Snip: print out current state
while True:
ok = get_input("Is this ok? (y/n)")
if ok.lower() == "y": break 2 # This doesn't work :(
if ok.lower() == "n": break
# Do more processing with menus and stuff
有没有办法使这项工作?或者我是否需要进行一次检查以跳出输入循环,然后进行另一项更有限的检查,以在用户满意的情况下一起跳出外部循环?
goto
一起使用很不错
我的第一直觉是将嵌套循环重构为一个函数并使用 return
来突破。
这是另一种简短的方法。缺点是你只能打破外循环,但有时它正是你想要的。
for a in xrange(10):
for b in xrange(20):
if something(a, b):
# Break the inner loop...
break
else:
# Continue if the inner loop wasn't broken.
continue
# Inner loop was broken, break the outer.
break
这使用了在 Why does python use 'else' after for and while loops? 中解释的 for / else 构造
关键见解:似乎外循环似乎总是中断。但是如果内循环没有中断,外循环也不会。
continue
语句在这里很神奇。它在 for-else 子句中。 By definition 如果没有内部中断,就会发生这种情况。在那种情况下,continue
巧妙地绕过了外部中断。
something(a, b)
也取决于 a
。只要 something(a, b)
不是 True
,外部循环就可以运行。
PEP 3136 建议标记中断/继续。 Guido rejected it 因为“需要此功能的代码如此复杂非常罕见”。 PEP 确实提到了一些解决方法(例如异常技术),而 Guido 认为在大多数情况下使用 return 重构会更简单。
return
通常是要走的路,但我已经看到很多情况,其中一个简单简洁的“break 2
”语句就非常有意义。此外,对于 continue
,refactor/return
的工作方式不同。在这些情况下,数字 break 和 continue 比重构为一个微小的函数、引发异常或涉及在每个嵌套级别设置一个要中断的标志的复杂逻辑更容易遵循并且不那么混乱。很遗憾Guido拒绝了它。
break; break
会很好。
break
转发到所有循环。愚蠢的。
首先,普通逻辑是有帮助的。
如果由于某种原因无法确定终止条件,则例外是一种备用计划。
class GetOutOfLoop( Exception ):
pass
try:
done= False
while not done:
isok= False
while not (done or isok):
ok = get_input("Is this ok? (y/n)")
if ok in ("y", "Y") or ok in ("n", "N") :
done= True # probably better
raise GetOutOfLoop
# other stuff
except GetOutOfLoop:
pass
对于此特定示例,可能不需要例外。
另一方面,我们在字符模式应用程序中经常有“Y”、“N”和“Q”选项。对于“Q”选项,我们希望立即退出。那更不寻常。
done
)。 (2) 引发异常。将它们合并成一个解决方案只会让它看起来很复杂。对于未来的读者:要么使用涉及 done
的所有行,要么定义 GetOutOfLoop(Exception)
并引发/除此之外。
Exception
构造函数(例如 raise Exception('bla bla bla')
)在 Python 2 和 Python 3 中都是有效的。在这种情况下,前者更可取因为我们不希望我们的 except
块捕获 所有 异常,而只捕获我们用来退出循环的特殊异常。如果我们按照您建议的方式做事,然后代码中的错误导致引发意外异常,则会被错误地视为故意退出循环。
我倾向于同意重构为函数通常是这种情况的最佳方法,但是当你真的需要打破嵌套循环时,这里是异常引发方法的一个有趣变体@S.Lott 描述的。它使用 Python 的 with
语句使异常引发看起来更好一些。使用以下命令定义一个新的上下文管理器(您只需执行一次):
from contextlib import contextmanager
@contextmanager
def nested_break():
class NestedBreakException(Exception):
pass
try:
yield NestedBreakException
except NestedBreakException:
pass
现在您可以按如下方式使用此上下文管理器:
with nested_break() as mylabel:
while True:
print "current state"
while True:
ok = raw_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y": raise mylabel
if ok == "n" or ok == "N": break
print "more processing"
优点:(1) 它稍微干净一些(没有显式的 try-except 块),以及 (2) 每次使用 nested_break
都会获得一个定制的 Exception
子类;无需每次都声明您自己的 Exception
子类。
引入一个您将用作“循环断路器”的新变量。首先为它分配一些东西(False,0,等等),然后在外部循环中,在你从中中断之前,将值更改为其他东西(True,1,...)。一旦循环退出,让“父”循环检查该值。让我演示一下:
breaker = False #our mighty loop exiter!
while True:
while True:
if conditionMet:
#insert code here...
breaker = True
break
if breaker: # the interesting part!
break # <--- !
如果你有一个无限循环,这是唯一的出路;对于其他循环的执行确实要快得多。如果您有许多嵌套循环,这也适用。您可以全部退出,也可以只退出一些。无限可能!希望这有帮助!
首先,您还可以考虑将获取和验证输入的过程变成一个函数;在该函数中,如果它正确,您可以只返回该值,如果不正确,则继续在 while 循环中旋转。这基本上消除了您解决的问题,并且通常可以应用于更一般的情况(打破多个循环)。如果您绝对必须在代码中保留此结构,并且真的不想处理簿记布尔值...
您还可以通过以下方式使用 goto(使用来自 here 的愚人节模块):
#import the stuff
from goto import goto, label
while True:
#snip: print out current state
while True:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y": goto .breakall
if ok == "n" or ok == "N": break
#do more processing with menus and stuff
label .breakall
我知道,我知道,“你不能使用 goto”等等,但它在像这样的奇怪情况下效果很好。
要跳出多个嵌套循环,而不重构为函数,请使用带有内置 StopIteration exception 的“模拟 goto 语句”:
try:
for outer in range(100):
for inner in range(100):
if break_early():
raise StopIteration
except StopIteration: pass
请参阅 this discussion,了解使用 goto 语句打破嵌套循环。
keeplooping = True
while keeplooping:
# Do stuff
while keeplooping:
# Do some other stuff
if finisheddoingstuff():
keeplooping = False
或类似的东西。
您可以在内循环中设置一个变量,并在内循环退出后立即在外循环中检查它,如果合适则中断。我有点喜欢 GOTO 方法,只要你不介意使用愚人节的笑话模块——它不是 Pythonic,但它确实有意义。
这不是最漂亮的方法,但在我看来,这是最好的方法。
def loop():
while True:
#snip: print out current state
while True:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y": return
if ok == "n" or ok == "N": break
#do more processing with menus and stuff
我很确定您也可以在这里使用递归来解决问题,但我不知道这对您来说是否是一个不错的选择。
如果两个条件为真,则继续循环。
我认为这是一种更 Pythonic 的方式:
dejaVu = True
while dejaVu:
while True:
ok = raw_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y" or ok == "n" or ok == "N":
dejaVu = False
break
while dejaVu:
?无论如何,您将其设置为true。
True
条件下跳过两个循环,但一个就足够了。
if not dejaVu: break
解决,从而退出主循环吗?我认为解决方案最接近所要求的。 +1
没有办法从语言层面做到这一点。有些语言有一个 goto 其他语言有一个带参数的中断,python 没有。最好的选择是:设置一个由外循环检查的标志,或设置外循环条件。将循环放在一个函数中,并使用 return 一次跳出所有循环。重新制定你的逻辑。
Credit goes to Vivek Nagarajan, Programmer since 1987
使用功能
def doMywork(data):
for i in data:
for e in i:
return
使用标志
is_break = False
for i in data:
if is_break:
break # outer loop break
for e in i:
is_break = True
break # inner loop break
将您的循环逻辑分解为一个迭代器,该迭代器产生循环变量并在完成后返回——这是一个简单的迭代器,它以行/列的形式排列图像,直到我们没有图像或无法放置它们:
def it(rows, cols, images):
i = 0
for r in xrange(rows):
for c in xrange(cols):
if i >= len(images):
return
yield r, c, images[i]
i += 1
for r, c, image in it(rows=4, cols=4, images=['a.jpg', 'b.jpg', 'c.jpg']):
... do something with r, c, image ...
这样做的好处是将复杂的循环逻辑和处理...
Python while ... else
结构中有一个隐藏的技巧,可用于模拟双中断,而无需太多代码更改/添加。实质上,如果 while
条件为假,则触发 else
块。 continue
或 break
都不会触发 else
块。有关详细信息,请参阅“Else clause on Python while statement”或 Python doc on while (v2.7) 的答案。
while True:
#snip: print out current state
ok = ""
while ok != "y" and ok != "n":
ok = get_input("Is this ok? (y/n)")
if ok == "n" or ok == "N":
break # Breaks out of inner loop, skipping else
else:
break # Breaks out of outer loop
#do more processing with menus and stuff
唯一的缺点是您需要将双重中断条件移动到 while
条件中(或添加标志变量)。 for
循环也存在这种变化,其中 else
块在循环完成后触发。
How to break out of multiple loops in Python?
,答案应该是“它不起作用,试试别的”。我知道它修复了 OP 的确切给定示例,但没有回答他们的问题。
将多个循环变成一个可破坏循环的简单方法是使用 numpy.ndindex
for i in range(n):
for j in range(n):
val = x[i, j]
break # still inside the outer loop!
for i, j in np.ndindex(n, n):
val = x[i, j]
break # you left the only loop there was!
您确实必须索引到您的对象,而不是能够显式地遍历这些值,但至少在简单的情况下,它似乎比大多数建议的答案简单 2-20 倍。
在这种情况下,正如其他人所指出的那样,功能分解是要走的路。 Python 3 中的代码:
def user_confirms():
while True:
answer = input("Is this OK? (y/n) ").strip().lower()
if answer in "yn":
return answer == "y"
def main():
while True:
# do stuff
if user_confirms():
break
另一种将迭代减少到单级循环的方法是使用在 python reference 中指定的生成器
for i, j in ((i, j) for i in A for j in B):
print(i , j)
if (some_condition):
break
您可以将其扩展到循环的任意数量的级别
缺点是你不能再只打破一个级别。要么全有,要么全无。
另一个缺点是它不适用于 while 循环。我最初想在 Python - `break` out of all loops 上发布此答案,但不幸的是,该答案已作为此答案的副本关闭
我想提醒您,Python 中的函数可以在代码中间创建,并且可以透明地访问周围的变量以进行读取,并使用 nonlocal
或 global
声明进行写入。
所以你可以使用一个函数作为一个“可破坏的控制结构”,定义一个你想要返回的地方:
def is_prime(number):
foo = bar = number
def return_here():
nonlocal foo, bar
init_bar = bar
while foo > 0:
bar = init_bar
while bar >= foo:
if foo*bar == number:
return
bar -= 1
foo -= 1
return_here()
if foo == 1:
print(number, 'is prime')
else:
print(number, '=', bar, '*', foo)
>>> is_prime(67)
67 is prime
>>> is_prime(117)
117 = 13 * 9
>>> is_prime(16)
16 = 4 * 4
通过使用函数:
def myloop():
for i in range(1,6,1): # 1st loop
print('i:',i)
for j in range(1,11,2): # 2nd loop
print(' i, j:' ,i, j)
for k in range(1,21,4): # 3rd loop
print(' i,j,k:', i,j,k)
if i%3==0 and j%3==0 and k%3==0:
return # getting out of all loops
myloop()
尝试通过注释掉 return
来运行上述代码。
不使用任何功能:
done = False
for i in range(1,6,1): # 1st loop
print('i:', i)
for j in range(1,11,2): # 2nd loop
print(' i, j:' ,i, j)
for k in range(1,21,4): # 3rd loop
print(' i,j,k:', i,j,k)
if i%3==0 and j%3==0 and k%3==0:
done = True
break # breaking from 3rd loop
if done: break # breaking from 2nd loop
if done: break # breaking from 1st loop
现在,首先按原样运行上述代码,然后尝试通过注释掉包含 break
的每一行从底部开始运行。
尝试使用无限生成器。
from itertools import repeat
inputs = (get_input("Is this ok? (y/n)") for _ in repeat(None))
response = (i.lower()=="y" for i in inputs if i.lower() in ("y", "n"))
while True:
#snip: print out current state
if next(response):
break
#do more processing with menus and stuff
# this version uses a level counter to choose how far to break out
break_levels = 0
while True:
# snip: print out current state
while True:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y":
break_levels = 1 # how far nested, excluding this break
break
if ok == "n" or ok == "N":
break # normal break
if break_levels:
break_levels -= 1
break # pop another level
if break_levels:
break_levels -= 1
break
# ...and so on
# this version breaks up to a certain label
break_label = None
while True:
# snip: print out current state
while True:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y":
break_label = "outer" # specify label to break to
break
if ok == "n" or ok == "N":
break
if break_label:
if break_label != "inner":
break # propagate up
break_label = None # we have arrived!
if break_label:
if break_label != "outer":
break # propagate up
break_label = None # we have arrived!
#do more processing with menus and stuff
这是一个似乎可行的实现:
break_ = False
for i in range(10):
if break_:
break
for j in range(10):
if j == 3:
break_ = True
break
else:
print(i, j)
唯一的缺点是您必须在循环之前定义 break_
。
我个人会做的是使用一个布尔值,当我准备好打破外循环时切换。例如
while True:
#snip: print out current state
quit = False
while True:
ok = input("Is this ok? (y/n)")
if ok.lower() == "y":
quit = True
break # this should work now :-)
if ok.lower() == "n":
quit = True
break # This should work too :-)
if quit:
break
#do more processing with menus and stuff
如果不喜欢重构为函数,可能像下面这样的小技巧会做
添加了 1 个 break_level 变量来控制 while 循环条件
break_level = 0
# while break_level < 3: # if we have another level of nested loop here
while break_level < 2:
#snip: print out current state
while break_level < 1:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y": break_level = 2 # break 2 level
if ok == "n" or ok == "N": break_level = 1 # break 1 level
您可以定义一个变量(例如 break_statement ),然后在发生两次中断条件时将其更改为不同的值,并在 if 语句中使用它来中断第二个循环。
while True:
break_statement=0
while True:
ok = raw_input("Is this ok? (y/n)")
if ok == "n" or ok == "N":
break
if ok == "y" or ok == "Y":
break_statement=1
break
if break_statement==1:
break
我来这里的原因是我有一个外循环和一个内循环,如下所示:
for x in array:
for y in dont_use_these_values:
if x.value==y:
array.remove(x) # fixed, was array.pop(x) in my original answer
continue
do some other stuff with x
如您所见,它实际上不会转到下一个 x,而是会转到下一个 y。
我发现解决这个问题的方法只是遍历数组两次:
for x in array:
for y in dont_use_these_values:
if x.value==y:
array.remove(x) # fixed, was array.pop(x) in my original answer
continue
for x in array:
do some other stuff with x
我知道这是 OP 问题的一个具体案例,但我发布它是希望它能帮助人们以不同的方式思考他们的问题,同时保持简单。
break
而不是 continue
就可以满足您的要求,不是吗? :-)
两种方式的解决方案
举个例子:这两个矩阵是否相等/相同? matrix1 和 matrix2 是相同大小、n 的二维矩阵。
第一个解决方案,没有功能
same_matrices = True
inner_loop_broken_once = False
n = len(matrix1)
for i in range(n):
for j in range(n):
if matrix1[i][j] != matrix2[i][j]:
same_matrices = False
inner_loop_broken_once = True
break
if inner_loop_broken_once:
break
第二种解决方案,具有功能
这是我的情况的最终解决方案。
def are_two_matrices_the_same (matrix1, matrix2):
n = len(matrix1)
for i in range(n):
for j in range(n):
if matrix1[i][j] != matrix2[i][j]:
return False
return True
为了尽量减少对 OP 问题的更改,我只是在中断第一个 for 循环之前添加了一个标志,并检查外循环上的该标志,看看我们是否需要再次制动。
break_2 = False
while True:
# Snip: print out current state
if break_2: break
while True:
ok = get_input("Is this ok? (y/n)")
if ok.lower() == "y": break_2 = True
if break_2: break
if ok.lower() == "n": break
# Do more processing with menus and stuff
我最近遇到了这个问题,为了避免重复的 return 语句,它可以隐藏逻辑错误,查看了@yak 的想法。这在嵌套的 for 循环中效果很好,但不是很优雅。另一种方法是在下一个循环之前检查条件:
b = None
for a in range(10):
if something(a, b): # should never = True if b is None
break
for b in range(20):
pass
这可能不适用于任何地方,但具有适应性,并且如果需要,具有允许复制条件而不是潜在结果的优势。
不定期副业成功案例分享
return
是正确的方法。其理由是,根据Python 之禅,“扁平优于嵌套”。我们这里有三层嵌套,如果这开始妨碍,是时候减少嵌套或至少将整个嵌套提取到它自己的函数中。