如何理解Python循环中的`else`子句?

192

许多Python程序员可能不知道 while 循环和 for 循环的语法中包含一个可选的 else:子句:

for val in iterable:
    do_something(val)
else:
    clean_up()
else子句中的语句是一些特定清理操作的好地方,并且在循环正常终止时执行:也就是说,用returnbreak退出循环会跳过else子句; 在continue之后退出执行它。我之所以知道这一点,仅仅因为我刚刚(再次)查了一下文档,因为我总是记不住何时执行else子句。

总是吗?在循环“失败”时,正如名称所示?在正常终止时?即使循环使用return退出?我永远无法确定,除非查询文档。

我把持续的不确定归咎于关键字的选择:对于这种语义,我发现else极难记忆。我的问题不是“为什么要使用此关键字来实现此目的”(尽管在阅读答案和评论后,我可能会投反对票),而是如何考虑else关键字使其语义具有意义,从而可以记住它?

我相信这方面有很多讨论,我可以想象选择是为了与try语句的else:子句保持一致(我也必须查阅它),并且不将其添加到Python保留字列表中。也许选择else的原因会澄清它的功能并使它更易记,但我关注将名称与功能连接起来,而不仅仅是历史解释。

这个问题的答案曾短暂地被关闭为重复,其中包含许多有趣的背景故事。我的问题有不同的焦点(如何将else的具体语义与关键字选择联系起来),但我觉得应该在某个地方链接到这个问题。


6
@alexis Dharman的编辑是为了使您的问题不基于个人意见,同时保持问题本身,我认为这次编辑使帖子好很多(且不值得关闭)。 - Nick stands with Ukraine
@Dharman,我很感激你的努力,但是你的编辑完全扭曲了问题的意图和内容。请停止。 - alexis
3
如何理解else关键字的语义,以便记住它?请注意,具体解释如何帮助您个人记住else的工作方式并不是一个有用的问题,因为如果没有这个问题,它会被认为是基于个人意见的。 - Nick stands with Ukraine
1
感谢您的解释,@nick。然而,Dharman的标题使它成为一个非常不同的问题的重复。如果您确信这太基于个人观点,我可以接受您的投票。但请不要动这个问题。 - alexis
1
还有一个问题是关于理解这个设计,而不是它的作用是什么。 - alexis
15个回答

4

else从循环结构中看作一部分;break完全跳出循环结构,因此跳过了else

但实际上,我的思维模式只是它是C/C++模式的“结构化”版本:

  for (...) {
    ...
    if (test) { goto done; }
    ...
  }
  ...
done:
  ...

所以当我遇到for...else或者自己写的时候,不是直接理解它,而是在脑海中将其翻译成上述模式的理解,然后找出哪些部分的Python语法与模式的哪些部分相对应。

(我用引号括起来的"结构化"并不意味着代码是否结构化,而仅仅是指是否有专门的关键字和语法用于特定的结构)


1
“else”在哪里?如果你想让“done:”标签代表“else:”,我认为你完全搞反了。 - alexis
@alexis 'else' 代码将填充在 done: 标签之前的 '...' 立即之前。总体对应关系可能最好这样表述:Python 具有循环中的 else 结构,因此您可以在没有 goto 的情况下表达此控制流模式。 - zwol
有其他执行此控制流程模式的方法,例如通过设置标志。这就是“else”的作用所在。 - alexis

2
如果将elsefor搭配使用,可能会让人感到困惑。我认为关键字else在这种语法中并不是一个好的选择,但如果将else与包含breakif搭配使用,你会发现它实际上很有意义。如果没有前置if语句,else几乎没有用处,我相信这就是语法设计者选择该关键字的原因。
让我用人类语言来演示一下。
对于一组嫌疑人中的每个人,如果有任何一个人是罪犯,请停止调查。否则报告失败。

1
我认为关键在于考虑“continue”的含义,而不是“else”。其他你提到的关键字会跳出循环(异常退出),而“continue”不会,它只是跳过循环内代码块的其余部分。它可以在循环终止之前执行是偶然的:实际上,终止是通过评估循环条件表达式来完成的。然后你只需要记住,在正常循环终止后执行“else”子句。

0

带有else子句的while语句

while condition:
    iteration
else:
    conclusion

完全等同于

while True:
    if not condition:
        conclusion
        break
    iteration

带有else子句的for语句

for item in iterable:
    iteration
else:
    conclusion

等价于

iterator = iter(iterable)
while True:
    try:
        item = next(iterator)
    except StopIteration:
        conclusion
        break
    iteration

它有助于理解迭代语句中 breakcontinue 语句的影响。

注意。 — 对于没有 else 子句的 whilefor 语句,在等效代码中用 pass 语句替换结论语句。


1
for循环的一个微妙之处在于它只捕获在执行next(iterator)时引发的StopIteration异常。如果迭代块内部有其他东西引发了StopIteration,那么该异常将会跳出循环(并且不会执行conclusion)。但是,要准确地编写这样的代码是很麻烦的(而不是使用while..else)。 - Beni Cherniavsky-Paskin
1
@BeniCherniavsky-Paskin非常感谢,我已经修复了代码(希望如此)。现在它实际上更容易阅读了。 - Géry Ogam

-1
# tested in Python 3.6.4
def buy_fruit(fruits):
    '''I translate the 'else' below into 'if no break' from for loop '''
    for fruit in fruits:
        if 'rotten' in fruit:
            print(f'do not want to buy {fruit}')
            break
    else:  #if no break
        print(f'ready to buy {fruits}')


if __name__ == '__main__':
    a_bag_of_apples = ['golden delicious', 'honeycrisp', 'rotten mcintosh']
    b_bag_of_apples = ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
    buy_fruit(a_bag_of_apples)
    buy_fruit(b_bag_of_apples)

'''
do not want to buy rotten mcintosh
ready to buy ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
'''

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接