我理解这种结构是如何运作的:

for i in range(10):
    print(i)

    if i == 9:
        print("Too big - I'm giving up!")
        break
else:
    print("Completed successfully")

但我不明白为什么在这里使用else作为关键字,因为它表明有问题的代码只在for块未完成时运行,这与它所做的相反!无论我怎么想,我的大脑都不能从for语句无缝地过渡到else语句块。对我来说,continue和continuouswith更有意义(我正试着训练自己这样读它)。

我想知道Python程序员是如何在头脑中(或者大声地,如果你喜欢)阅读这个结构的。也许我遗漏了一些可以使这些代码块更容易破译的东西?


这个问题是关于底层设计决策的,也就是说,为什么能够编写这些代码是有用的。另请参阅Python while语句中的Else子句,了解语法含义的具体问题。


当前回答

我把它读成这样:“当可迭代对象完全耗尽时,在完成for语句后,执行即将继续执行下一个语句,else子句将被执行。”因此,当迭代被break打断时,这将不会执行。

其他回答

Raymond Hettinger做了一个非常棒的演讲,题为“将代码转换成美丽的、地道的Python”,在演讲中他简要地介绍了for Python的历史。其他结构。相关部分是“在循环中区分多个出口点”,从15:50开始,持续约3分钟。以下是要点:

for…else构造是由Donald Knuth设计的,作为某些GOTO用例的替代; 重用else关键字是有意义的,因为“它是Knuth使用的,人们知道,在那个时候,所有的(for语句)都在下面嵌入了一个if和GOTO,他们期望else;” 事后看来,它应该被称为“nobreak”(或者可能是“nobreak”),这样就不会让人困惑了

所以,如果问题是“为什么他们不改变这个关键字?”,那么Cat Plus Plus可能会给出最准确的答案——在这一点上,它对现有代码的破坏性太大了,不太实用。但如果你真正想问的问题是为什么其他东西一开始就被重用,嗯,显然这在当时看起来是个好主意。

就我个人而言,我喜欢折衷的注释# no break in-line,因为else可能会被误认为属于循环内部。它相当清晰简洁。Bjorn在他的回答结尾链接的摘要中简要提到了这个选项:

为了完整起见,我应该提一下 语法,想要这个语法的程序员现在就可以拥有它: 项目顺序如下: 过程(项目) Else: #不休息 套件


*视频中那部分的额外引用:“就像我们调用lambda makefunction一样,没有人会问,‘lambda是做什么的?’”

一个常见的构造是运行一个循环,直到找到一些东西,然后跳出循环。问题是,如果我跳出循环或循环结束,我需要确定发生了哪种情况。一种方法是创建一个标志或存储变量,让我进行第二次测试,以查看循环是如何退出的。

例如,假设我需要在列表中搜索并处理每个项,直到找到标志项,然后停止处理。如果缺少标志项,则需要引发异常。

使用Python进行…你的其他构念

for i in mylist:
    if i == theflag:
        break
    process(i)
else:
    raise ValueError("List argument missing terminal flag.")

将此方法与不使用此语法糖的方法进行比较:

flagfound = False
for i in mylist:
    if i == theflag:
        flagfound = True
        break
    process(i)

if not flagfound:
    raise ValueError("List argument missing terminal flag.")

在第一种情况下,raise与它所使用的for循环紧密绑定。第二种情况是,绑定不那么牢固,在维护过程中可能会出现错误。

由于技术部分已经回答了很多,我的评论只是与产生这个循环关键字的混淆有关。

由于Python是一种非常雄辩的编程语言,关键字的误用更加臭名昭著。else关键字完美地描述了决策树的部分流程,“如果你不能做这个,(else)做那个”。这在我们自己的语言中是隐含的。

相反,与while和for语句一起使用这个关键字会造成混淆。原因是,我们作为程序员的职业生涯告诉我们else语句存在于决策树中;它的逻辑作用域是一个有条件地返回要遵循的路径的包装器。同时,循环语句有一个象征性的明确目标来达到某些东西。目标是在流程的连续迭代之后实现的。

If / else表示要遵循的路径。循环遵循一条路径,直到“目标”完成。

问题是else这个词清楚地定义了条件中的最后一个选项。这个词的语义是Python和人类语言共享的。但是在人类语言中,这个词从来没有用来表示某人或某物在某事完成后将采取的行动。如果在完成它的过程中出现了问题(更像是break语句),就会使用它。

最后,关键字将保留在Python中。很明显这是一个错误,当每个程序员都试图像记忆工具一样想出一个故事来理解它的用法时,就更清楚了。如果他们选择了关键词,我会很喜欢的。我相信这个关键字非常适合迭代流程,即循环后的收益。

这就像有些孩子组装玩具的每一步都会遇到的情况:然后呢,爸爸?

即使对经验丰富的Python程序员来说,这也是一个奇怪的结构。当与for-loops结合使用时,它的基本意思是“在可迭代对象中找到某个项,否则如果没有找到则do…”。如:

found_obj = None
for obj in objects:
    if obj.key == search_key:
        found_obj = obj
        break
else:
    print('No object found.')

但是无论何时你看到这个结构,一个更好的选择是将搜索封装在一个函数中:

def find_obj(search_key):
    for obj in objects:
        if obj.key == search_key:
            return obj

或者使用列表推导式:

matching_objs = [o for o in objects if o.key == search_key]
if matching_objs:
    print('Found {}'.format(matching_objs[0]))
else:
    print('No object found.')

它在语义上并不等同于其他两个版本,但在非性能关键代码中工作得足够好,在这些代码中,是否迭代整个列表并不重要。其他人可能不同意,但我个人会避免在生产代码中使用for-else或while-else块。

另参见[Python-ideas] for…其他线程

很好的回答是:

这就解释了历史,而且 这就赋予了 引用以简化您的翻译/理解。

我在这里的注释来自Donald Knuth曾经说过(抱歉找不到参考),有一个结构,其中while-else与if-else无法区分,即(在Python中):

x = 2
while x > 3:
    print("foo")
    break
else:
    print("boo")

具有相同的流程(不包括低级别差异):

x = 2
if x > 3:
    print("foo")
else:
    print("boo")

关键是if-else可以被认为是while-else的语法糖,它在if块的末尾有隐含的break。相反的含义是,while循环是if的扩展,更常见(它只是重复/循环的条件检查),因为if经常在while之前教授。然而,这不是真的,因为这将意味着else块在while-else每次当condition为false时执行。

为了便于理解,可以这样想:

没有break, return等,循环只在condition不再为真时结束,在这种情况下else块也将执行一次。在Python for的情况下,你必须考虑c风格的for循环(带条件)或将它们转换为while。

另注:

循环中的过早中断、返回等使得condition不可能变为false,因为当condition为true时,执行跳出了循环,并且它永远不会再回来检查它。