给定以下代码(不起作用):
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
有办法解决这个问题吗?还是我必须先检查一次,跳出输入循环,然后再检查一次,如果用户满意,再跳出外部循环?
在Python中有一个隐藏的技巧…Else结构,可以用来模拟双中断,而不需要太多的代码更改/添加。本质上,如果while条件为false,则触发else块。任何异常、continue或break都不会触发else块。有关更多信息,请参阅对“Python while语句上的Else子句”或Python while上的doc (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块在循环完成后被触发。
首先,普通的逻辑是有用的。
如果由于某种原因,终止条件无法确定,例外是一个后备计划。
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"选项,我们要立即退出。这是比较特殊的。
由于这个问题已经成为进入特定循环的标准问题,我想用Exception给出我的答案。
虽然在多循环构造中不存在名为“循环中断”的标签,但我们可以使用用户定义异常来中断到我们选择的特定循环。考虑下面的例子,让我们在6进制编号系统中打印所有最多4位的数字:
class BreakLoop(Exception):
def __init__(self, counter):
Exception.__init__(self, 'Exception 1')
self.counter = counter
for counter1 in range(6): # Make it 1000
try:
thousand = counter1 * 1000
for counter2 in range(6): # Make it 100
try:
hundred = counter2 * 100
for counter3 in range(6): # Make it 10
try:
ten = counter3 * 10
for counter4 in range(6):
try:
unit = counter4
value = thousand + hundred + ten + unit
if unit == 4 :
raise BreakLoop(4) # Don't break from loop
if ten == 30:
raise BreakLoop(3) # Break into loop 3
if hundred == 500:
raise BreakLoop(2) # Break into loop 2
if thousand == 2000:
raise BreakLoop(1) # Break into loop 1
print('{:04d}'.format(value))
except BreakLoop as bl:
if bl.counter != 4:
raise bl
except BreakLoop as bl:
if bl.counter != 3:
raise bl
except BreakLoop as bl:
if bl.counter != 2:
raise bl
except BreakLoop as bl:
pass
当我们打印输出时,我们永远不会得到任何单位位是4的值。在这种情况下,在同一个循环中引发BreakLoop(4)并捕获时,我们不会中断任何循环。类似地,当十位有3时,我们使用BreakLoop(3)进入第三个循环。当百位是5时,我们使用BreakLoop(2)进入第二个循环,当千位是2时,我们使用BreakLoop(1)进入第一个循环。
简而言之,在内部循环中引发异常(内置或用户定义),并在循环中从您想恢复控件的位置捕获它。如果想从所有循环中中断,可以在所有循环之外捕获异常。(我没有举例说明)。
首先,您还可以考虑将获取和验证输入的过程作为一个函数;在该函数中,如果值正确,则返回值,如果不正确,则继续在while循环中旋转。这本质上避免了您解决的问题,并且通常可以应用于更一般的情况(打破多个循环)。如果你一定要在代码中保留这个结构,并且真的不想处理记帐布尔值……
你也可以用下面的方式使用goto(在这里使用一个愚人节模块):
#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”之类的,但它在这种奇怪的情况下很管用。