给定以下代码(不起作用):
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
有办法解决这个问题吗?还是我必须先检查一次,跳出输入循环,然后再检查一次,如果用户满意,再跳出外部循环?
由于这个问题已经成为进入特定循环的标准问题,我想用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)进入第一个循环。
简而言之,在内部循环中引发异常(内置或用户定义),并在循环中从您想恢复控件的位置捕获它。如果想从所有循环中中断,可以在所有循环之外捕获异常。(我没有举例说明)。
我来这里的原因是我有一个外循环和一个内循环,像这样:
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问题的一个具体案例,但我发布它是希望它能帮助人们以不同的方式思考他们的问题,同时保持事情简单。
我倾向于认为重构到函数中通常是这种情况的最佳方法,但是当您确实需要打破嵌套循环时,这里有一个有趣的异常引发方法的变体@S。洛特。它使用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子类。