630

我了解此构造的工作原理:

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或者continuewith更有意义(我正在努力训练自己阅读它)。

我想知道 Python 编码人员是如何在他们的脑海中阅读这个结构的(或者大声,如果你愿意的话)。也许我错过了一些可以让这些代码块更容易被破译的东西?

4

23 回答 23

786

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

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

使用您拥有的 Python for...else构造

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 循环。在第二种情况下,绑定没有那么强,并且在维护期间可能会引入错误。

于 2012-04-02T17:13:15.520 回答
369

即使对于经验丰富的 Python 编码人员来说,这也是一个奇怪的结构。当与 for 循环结合使用时,它的基本意思是“在可迭代中找到一些项目,否则如果没有找到,请执行...”。如:

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...else 线程摘要

于 2012-04-02T16:30:48.063 回答
226

Raymond Hettinger 有一篇精彩的演讲,题为“将代码转化为漂亮的惯用 Python ”,他在其中简要介绍了该for ... else构造的历史。相关部分是“区分循环中的多个出口点” ,从 15:50 开始,持续约三分钟。以下是要点:

  • for ... else构造是由 Donald Knuth 设计的,用于替代某些GOTO用例;
  • 重用else关键字是有道理的,因为“这是 Knuth 使用的,人们知道,当时所有 [for语句] 都嵌入了 anifGOTO下面,他们期望else;”
  • 事后看来,它应该被称为“no break”(或者可能是“nobreak”),这样就不会令人困惑了。*

所以,如果问题是,“他们为什么不改变这个关键字?” 那么Cat Plus Plus 可能给出了最准确的答案——在这一点上,它对现有代码的破坏性太大而无法实用。但是,如果您真正要问的问题是为什么else首先要重用,那么显然这在当时似乎是个好主意。

就个人而言,我喜欢在可能被误认为属于循环内部的# no break任何地方进行内联评论的折衷方案。else它相当清晰和简洁。这个选项在Bjorn在他的答案末尾链接的摘要中得到了简短的提及:

为了完整起见,我应该提一下,只要稍微改变一下语法,想要这种语法的程序员现在就可以拥有它:

for item in sequence:
    process(item)
else:  # no break
    suite

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

于 2014-05-19T22:30:55.760 回答
56

为了简单起见,你可以这样想;

  • 如果它在循环中遇到break命令,则不会调用该部分。forelse
  • 如果在循环中没有遇到该break命令,则调用该部分。forelse

换句话说,如果 for 循环迭代没有被“破坏” break,则该else部分将被调用。

于 2017-10-27T11:03:41.780 回答
38

因为他们不想在语言中引入新的关键字。每个人都会窃取一个标识符并导致向后兼容性问题,因此它通常是最后的手段。

于 2012-04-02T16:20:03.813 回答
23

我认为文档对 else有很好的解释,继续

[...] 当循环因列表用尽而终止时(使用 for)或条件变为假(使用 while)时执行,但不会在循环被 break 语句终止时执行。”

来源:Python 2 文档:控制流教程

于 2014-05-13T08:28:54.667 回答
18

我发现“获取” for/else 所做的最简单的方法,更重要的是,何时使用它,是专注于 break 语句跳转到的位置。For/else 构造是单个块。break 跳出块,因此跳过了 else 子句。如果 else 子句的内容只是简单地跟在 for 子句之后,它永远不会被跳过,因此必须通过将其放在 if 中来提供等效的逻辑。之前已经说过,但不是完全用这些话,所以它可能对其他人有所帮助。尝试运行以下代码片段。为了清楚起见,我全心全意地赞成“不休息”的评论。

for a in range(3):
    print(a)
    if a==4: # change value to force break or not
        break
else: #no break  +10 for whoever thought of this decoration
    print('for completed OK')

print('statement after for loop')
于 2015-02-05T12:54:02.093 回答
14

我读到它是这样的:

如果仍然有条件运行循环,则做一些事情,否则做其他事情。

于 2012-04-02T16:20:33.567 回答
14

由于技术部分已经得到了相当多的回答,我的评论只是与产生这个回收关键字的混淆有关。

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

相反,将此关键字与whileandfor语句一起使用会造成混淆。原因是,我们作为程序员的职业告诉我们,else语句位于决策树中;它的逻辑范围,一个有条件地返回要遵循的路径的包装器。同时,循环语句有一个象征性的明确目标来达到某些目标。在一个过程的不断迭代之后,目标才能实现。

if / else 指明要遵循的路径。循环遵循一条路径,直到“目标”完成

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

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

这类似于某些孩子在组装玩具的每一步之后所遇到的情况:然后爸爸呢?

于 2016-01-12T18:12:41.980 回答
11

很好的答案是:

  • 解释了历史,并且
  • 提供了正确的引用来简化您的翻译/理解。

我在这里的注释来自 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 的语法糖,它在其块break的末尾隐含。if相反的含义,即while循环是对 的扩展if,更常见(它只是重复/循环的条件检查),因为if经常在while. 然而,这不是真的,因为这意味着每次条件为假时else都会执行 while-else 中的块。

为了便于理解,请这样想:

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

另一个注意事项:

循环内的Premature break,return等使条件不可能变为假,因为在条件为真时执行跳出循环,并且永远不会再回来检查它。

于 2019-02-21T16:57:18.757 回答
7

我把它读成“当iterable完全用尽,并且在完成后将执行下for一条语句时,将执行else子句。” 因此,当迭代被 中断时break,这将不会被执行。

于 2014-07-07T04:32:57.717 回答
7

我想知道 Python 编码人员是如何在他们的脑海中阅读这个结构的(或者大声,如果你愿意的话)。

我只是在脑海里想:

“否则没有break遇到……”

就是这样!

这是因为只有在循环中没有遇到语句时else才会执行该子句。breakfor

参考:

见这里:https ://book.pythontips.com/en/latest/for__else.html#else-clause (强调添加,“not”改为“NOT”):

for循环还有一个else我们大多数人都不熟悉的子句。else子句在循环正常完成后执行。这意味着循环没有遇到 break 语句。


话虽如此,我建议不要使用该语言的这种不寻常的功能。不要else在 for 循环之后使用该子句。这让大多数人感到困惑,只会减慢他们阅读和理解代码的能力。

于 2021-03-24T05:39:47.487 回答
6

我同意,这更像是“elif not [condition(s) raise break]”。

我知道这是一个旧线程,但我现在正在研究同一个问题,我不确定是否有人以我理解的方式捕捉到这个问题的答案。

对我来说,有三种“阅读” elsein For... elseorWhile... else语句的方法,它们都是等效的,它们是:

  1. else == if the loop completes normally (without a break or error)
  2. else == if the loop does not encounter a break
  3. else == else not (condition raising break)(想必有这样的条件,不然你就没有循环了)

所以,本质上,循环中的“else”实际上是一个“elif ...”,其中“...”是(1)没有中断,相当于(2)NOT [condition(s) raise break]。

我认为关键是else没有'break'是没有意义的,所以afor...else包括:

for:
    do stuff
    conditional break # implied by else
else not break:
    do more stuff

因此,for...else循环的基本元素如下,您可以用更简单的英语阅读它们:

for:
    do stuff
    condition:
        break
else: # read as "else not break" or "else not condition"
    do more stuff

正如其他海报所说,当您能够找到循环正在寻找的内容时,通常会引发中断,因此else:变成“如果找不到目标项目该怎么办”。

例子

您还可以一起使用异常处理、中断和 for 循环。

for x in range(0,3):
    print("x: {}".format(x))
    if x == 2:
        try:
            raise AssertionError("ASSERTION ERROR: x is {}".format(x))
        except:
            print(AssertionError("ASSERTION ERROR: x is {}".format(x)))
            break
else:
    print("X loop complete without error")

结果

x: 0
x: 1
x: 2
ASSERTION ERROR: x is 2
----------
# loop not completed (hit break), so else didn't run

例子

一个简单的例子,中断被击中。

for y in range(0,3):
    print("y: {}".format(y))
    if y == 2: # will be executed
        print("BREAK: y is {}\n----------".format(y))
        break
else: # not executed because break is hit
    print("y_loop completed without break----------\n")

结果

y: 0
y: 1
y: 2
BREAK: y is 2
----------
# loop not completed (hit break), so else didn't run

例子

没有中断,没有引发中断的条件,也没有遇到错误的简单示例。

for z in range(0,3):
     print("z: {}".format(z))
     if z == 4: # will not be executed
         print("BREAK: z is {}\n".format(y))
         break
     if z == 4: # will not be executed
         raise AssertionError("ASSERTION ERROR: x is {}".format(x))
else:
     print("z_loop complete without break or error\n----------\n")

结果

z: 0
z: 1
z: 2
z_loop complete without break or error
----------
于 2016-01-06T17:40:15.687 回答
6

关键字在else这里可能会造成混淆,正如许多人所指出的,类似nobreak,notbreak更合适。

为了for ... else ...从逻辑上理解,将其与try...except...else, not进行比较if...else...,大多数python程序员都熟悉以下代码:

try:
    do_something()
except:
    print("Error happened.") # The try block threw an exception
else:
    print("Everything is find.") # The try block does things just find.

同样,可以将其break视为一种特殊的Exception

for x in iterable:
    do_something(x)
except break:
    pass # Implied by Python's loop semantics
else:
    print('no break encountered')  # No break statement was encountered

区别是python暗示except break,你不能写出来,所以它变成:

for x in iterable:
    do_something(x)
else:
    print('no break encountered')  # No break statement was encountered

是的,我知道这种比较可能既困难又令人厌烦,但它确实澄清了困惑。

于 2017-04-27T03:36:06.960 回答
5

语句块中的代码else将在for循环未被中断时执行。

for x in xrange(1,5):
    if x == 5:
        print 'find 5'
        break
else:
    print 'can not find 5!'
#can not find 5!

来自文档:break 和 continue 语句,以及循环上的 else 子句

循环语句可能有一个 else 子句;它在循环因列表用尽而终止时(使用 for)或条件变为假(使用 while)时执行,但不是在循环由 break 语句终止时执行。下面的循环就是一个例子,它搜索素数:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             break
...     else:
...         # loop fell through without finding a factor
...         print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

(是的,这是正确的代码。仔细观察:else 子句属于 for 循环,而不是 if 语句。)

当与循环一起使用时,else 子句与 try 语句的 else 子句相比与 if 语句有更多的共同点:try 语句的 else 子句在没有异常发生时运行,而循环的 else 子句在没有中断发生时运行. 有关 try 语句和异常的更多信息,请参阅处理异常。

同样从 C 中借用的 continue 语句继续循环的下一次迭代:

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print("Found an even number", num)
...         continue
...     print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
于 2016-08-26T04:42:30.600 回答
5

这是我在上面没有看到其他人提到的一种思考方式:

首先,请记住 for 循环基本上只是 while 循环周围的语法糖。例如,循环

for item in sequence:
    do_something(item)

可以(大约)重写为

item = None
while sequence.hasnext():
    item = sequence.next()
    do_something(item)

其次,请记住,while 循环基本上只是重复的 if 块!您始终可以将 while 循环理解为“如果此条件为真,则执行主体,然后返回并再次检查”。

所以 while/else 非常有意义:它与 if/else 的结构完全相同,增加了循环直到条件变为 false 的功能,而不是只检查一次条件。

然后 for/else 也很有意义:因为所有 for 循环都只是 while 循环之上的语法糖,你只需要弄清楚底层 while 循环的隐式条件是什么,然后 else 对应于那个时候条件变为 False。

于 2018-12-11T17:52:30.190 回答
3
for i in range(3):
    print(i)

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

这里的“else”非常简单,只是意思

1、“如果for clause完成”

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

    if i == 2:
        print("Too big - I'm giving up!")
        break;
if "for clause is completed":
    print("Completed successfully")

写“for 子句完成”之类的长语句很实用,所以他们引入了“else”。

else这本质上是一个如果。

2,但是,怎么样for clause is not run at all

In [331]: for i in range(0):
     ...:     print(i)
     ...: 
     ...:     if i == 9:
     ...:         print("Too big - I'm giving up!")
     ...:         break
     ...: else:
     ...:     print("Completed successfully")
     ...:     
Completed successfully

所以它的完全陈述是逻辑组合:

if "for clause is completed" or "not run at all":
     do else stuff

或者这样说:

if "for clause is not partially run":
    do else stuff

或者这样:

if "for clause not encounter a break":
    do else stuff
于 2018-08-25T04:29:46.583 回答
2

这是除了搜索之外的另一个惯用用例。假设您想等待某个条件为真,例如在远程服务器上打开一个端口,以及一些超时。然后你可以使用这样的while...else构造:

import socket
import time

sock = socket.socket()
timeout = time.time() + 15
while time.time() < timeout:
    if sock.connect_ex(('127.0.0.1', 80)) is 0:
        print('Port is open now!')
        break
    print('Still waiting...')
else:
    raise TimeoutError()
于 2018-03-02T14:48:11.507 回答
2

我只是想自己重新弄明白。我发现以下有帮助!

• 将else视为与循环内部配对if(而不是与for) - 如果满足条件则中断循环,否则执行此操作 - 除非它else与多个 s 配对if
• 如果根本if不满足 s,则执行else
• 多个ifs 实际上也可以被认为是if- elifs!

于 2018-12-11T15:06:54.580 回答
1

你可以把它想象 else成其他的东西,或者其他的东西,这不是在循环中完成的。

于 2012-04-02T16:25:26.833 回答
1
for i in range(10):
    print(i)

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

break 关键字用于结束循环。如果 i = 9 则循环将结束。如果任何条件都不太满意,那么else将做剩下的部分。

于 2021-03-16T10:57:55.127 回答
0

else 子句在循环正常完成后执行。这意味着 仅当循环未被 break 语句终止时才执行 for/while 之后的 :==> else 块

for item in lista:
if(obj == item ):
    print("if True then break will run and else not run")
    break;
else:
print("in  else => obj not fount ")
于 2021-09-05T17:24:42.123 回答
-2

我认为结构为 for (if) A else B,而 for(if)-else 是一个特殊的 if-else大致上理解else可能会有所帮助。

A和B最多执行一次,与if-else结构相同。

for(if) 可以被认为是一个特殊的 if,它执行一个循环来尝试满足 if 条件。一旦满足if条件,A 和break否则,B.

于 2019-02-13T18:41:45.683 回答