在for 和while 循环体后避免使用else语句块
Python
的循环有一个其他编程语言中见不到的额外的特征:你可以在一个循环语句块之后立即的添加一个else
语句块。
for i in range(3):
print("Loop in %d!"% i )
else:
print("Else Block!")
>>>
Loop in 0
Loop in 1
Loop in 2
Else Block!
让人惊讶的是,当循环体运行结束的那一刻else
语句块就会立即得到执行。那为什么起名为else
块而不是“and
”语句块呢?在的if/else
语句中,else
意味着“当if
的那个条件不满足是,才执行这个语句块”。在try/except
语句块中,except
则意味着相同的定义,“只有当try
块内出现异常的时候才会执行except
内部的代码”。
相似地,在try/except/else
语句块中的else
也遵循上面的那个模式(详见第13项:从try/except/else/finally
语句块中受益)。因为这意味着“在如果前面的语句块没有失败的话就执行这个块”。try/filally
仍然很直观,含义为“总是能够在前面的try
块结束之后执行finally
语句块”。
鉴于Python
中所使用的else
,except
,finally
块,一个新的编程语言可能会假定在for/else
块中的else
含义为:“如果循环体没有正常的结束就执行else
语句”。 然而事实上,含义恰恰相反。再循环体中使用break
语句可以真正地跳过else
块。
for i in range(3):
print('Loop %d' % i)
if i == 1:
break
else:
print('else block')
>>>
Loop 0
Loop 1
另一个让人惊讶的地方就是:如果你便利一个空序列的话else
语句块就会立即得到执行。
for x in []:
print('Never runs')
else:
print('else block')
>>>
else block
对于这个行为比较合理的解释就是:位于循环块之后的else
语块会在通过循环查找一些东西时非常的有用。例如:你想查看两个数是否互为质数(公共的除数只能是1
)。这里,我迭代了每一个可能的公约数来测试这些数据。尝试了每一个选择后,循环结束。当两个数互为质数的时候else
语块就会得到执行,因为再循环体中并没有触发break
,所以互为质数。
a = 4
b = 9
for i in range(2, min(a, b) + 1):
print('Testing', i)
if a % i == 0 and b % i == 0:
print('not coprime')
break
else:
print('coprime')
>>>
Testing 2
Testing 3
Testing 4
coprime
在实际的使用中,你不可能像这样来书写代码,你可能会写一个工具函数来计算。常见的两种形式如下:
第一个是一旦发现正在寻找的条件就返回。如果遍历完整个循环则返回默认的输出。
def coprime(a, b): for i in range(2, min(a, b) + 1): if a % i == 0 and b % i == 0: return False return True
第二个是申请一个结果变量来表明你是否在循环中发现正在寻找的条件,一旦发现就通过
break
跳出循环来返回。def coprime2(a, b): is_coprime = True for i in range(2, min(a, b) + 1): if a % i == 0 and b % i == 0: is_coprime = False break return is_coprime
面对如此逻辑的代码。使用以上两种方法都会给读者一个清晰简洁的阅读体验。你从中获得的就是一个良好的阅读体验,想象一下这样做是否能够胜出改善添加else
语块而导致的今后代码上阅读难度就明白了。像循环这种简单的结构在Python
中是不言而喻的了,你应该避免在循环块的后面使用else
语句。
备忘录
Python
有用特殊的语法能够让else
语块在循环体结束的时候立刻得到执行。- 循环体后的
else
语块只有在循环体没有触发break
语句的时候才会执行。 - 避免在循环体的后面使用
else
语块,因为这样的表达不直观,而且容易误导读者。