在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中所使用的elseexceptfinally块,一个新的编程语言可能会假定在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语块,因为这样的表达不直观,而且容易误导读者。

results matching ""

    No results matching ""