以前学python的时候一度对如何跳出多层循环比较困惑,总觉得所有方法都不够pythonic。今天突然想起这茬,就觉得写篇文章讨论一下这个问题。
先来看看python中的goto是怎么写的
from goto import goto, label
for i in range(9):
for j in range(9):
for k in range(9):
print("I am trapped, please rescue!")
if k == 2:
goto .breakout # 从多重循环中跳出
label .breakout
print("Freedom!")
看完代码,不要急着复制出来运行,也不要急着pip install goto
。首先这段代码在你的环境里肯定是跑不起来的,不管是python2.7还是python3,都不行,因为它属于古老的python2.3,而且即使在古老的2.3中,它也是作为一个愚人节玩笑存在的,并不是真的让你在生产环境里用goto的。
既然代码都贴了,顺便贴一下运行结果吧
I'm trapped, please rescue!
I'm trapped, please rescue!
Freedom!
实在非要用goto的话,就看这里https://github.com/snoack/python-goto。这个项目的作者也表示了There is another module that has been released as April Fool's joke in 2004.
goto这个东西吧,就像泡面,偶尔来上一碗,香气扑鼻,但要你天天吃,那就恶心了。很多人都会教你千万别用goto,但依我浅见,这个东西偶尔用一下还是挺香的。
上面聊了半天goto,害得我忘记了这篇文章是要讲跳出多层循环的,回到主题。通常情况下,我会选择将多层循环封装成一个函数,然后在需要跳出循环的地方return。照这个道理把上面那段代码重构一下:
def foo():
def sub_foo():
for i in range(9):
for j in range(9):
for k in range(9):
print("I am trapped, please rescue!")
if k == 2:
return
sub_foo()
print("Freedom!")
foo()
虽然这么多缩进看起来有点恶心,但我觉得这是跳出多层循环最pythonic的写法了。
与return较为类似的,就是使用try-except,需要跳出的地方raise一下。但在我心目中raise是用来抛出异常的,用它来跳出循环的话,那就和科比的投篮选择一样,不合理。但是python官方文档在解释为什么不提供goto的时候,就抬出了使用raise来替代的方法。所以谈不上对错吧,用不用就看个人代码风格了。
合理使用else,也能顺利的跳出多层循环,但是代码可读性有点闹心,look:
for i in range(9):
for j in range(9):
for k in range(9):
print("I am trapped, please rescue!")
if k == 2:
break
else:
continue
break
else:
continue
break
print("Freedom!")
是不是看着有点闹心,这种代码的可读性是比较差的,别说其他人了,你写完隔一个月自己都搞不清这段逻辑。
答应我,别立flag,好吗。
在flag和goto之间二选一来跳出循环,我会选择goto。
Q:如何维护使用大量flag的代码?
A:加入更多的flag。
不知道是不是深受谭浩强影响的缘故,好多恶臭的代码都会充满一堆不明所以的flag,而且非要把它命名成flag吗?flag1,flag2,到flag正无穷,求求你当个人吧,别这么写了。
我觉得这样的flag尤其恶劣:
if k == 2:
flag = True
不行给它换个名字呗
if k == 2:
is_k_eq_2 = True
虽然这样有点长有点搓,但起码一看就知道这个标志用于记录k是否等于2。等等,我怎么开始写代码整洁之道了……