Python中的while语句的else子句

404
我注意到以下代码在Python中是合法的。我的问题是为什么?有特定的原因吗?
n = 5
while n != 0:
    print n
    n -= 1
else:
    print "what the..."

许多初学者在将if/else块嵌套在whilefor循环中并未正确缩进else时,会意外地遇到这种语法。解决方法是确保else块与if对齐,假设您的目的是将它们匹配。本问题解释了为什么它不会导致语法错误以及生成的代码含义。有关报告语法错误的情况,请参见我得到了一个IndentationError。我该如何修复它?

有关如何充分利用此功能的问题,请参见为什么python在for和while循环之后使用“else”?


13
这是因为大多数人都避免使用这个结构。 :) 我认为Guido在Py3k过程中提到,至少,在这个用法上选择单词else是一个非常糟糕的想法,并且他们不会再做这样的事情了。 - Nicholas Knight
6
@Nicholas Knight - 是的,尽管很诱人,但只有我能够在第一眼理解它。其他可怜的家伙可能需要查看语言规范,或者回到过去在这里发布一个问题 - 喂... - detly
9
选择使用 "else" 的想法是,这种结构通常与 while 循环中的 "if X: break" 结合使用。由于如果我们没有跳出循环,则会执行 "else" 子句,它类似于 "if" 的一种“否则”情况。请注意,这里的“else”指的是Python语言中while循环结构的关键字而不是英语中的“否则”。 - Jonathan Hartley
22
他们应该把它改名为“after:”。 - naught101
6
@naught101 - "after"这个关键字也不太合适,因为如果你使用“break”或“return”退出循环,它将不会执行。我总是告诉那些记不住用法的人,“如果你要跳出循环,你可以在break语句之前做一些事情。如果你正在遍历集合(在for循环中)或者打破了循环条件(在while循环中),'else'子句就是你完成收尾工作的地方。” - AusIV
显示剩余5条评论
13个回答

479
else子句仅在while条件变为false时执行。如果你从循环中break出来,或者发生异常,它将不会被执行。
可以将其视为与条件相关的if / else结构的一种方式:
if condition:
    handle_true()
else:
    handle_false()

类似于循环结构:

while condition:
    handle_true()
else:
    # condition is false now, handle and go on with the rest of the program
    handle_false()

一个示例可能是这样的:
while value < threshold:
    if not process_acceptable_value(value):
        # something went wrong, exit the loop; don't pass go, don't collect 200
        break
    value = update(value)
else:
    # value >= threshold; pass go, collect 200
    handle_threshold_reached()

76
当while循环的条件变为false时,else子句才会被执行。这里的措辞暗示了当while语句从true状态变为false状态时,else将被执行。然而,如果while从未进入true状态,else子句仍将被执行。 - user597608
2
pseudocode 所以,如果我说错了,请纠正我,但这与 while{} something 完全相同,唯一的区别是,如果在 while 循环中使用 break,则将跳过 something - Daniel Kaplan
2
可能最精确的伪代码是:while(True) { if (cond) { handle_true(); } else { handle_false(); break; } } - VinGarcia
1
即使while循环的条件变为false,如果循环以'break'结束,则else子句也不会被执行。例如,在'break'之前将条件更改为false。 - starriet
1
如果break被执行,那么条件就永远不会被判断为假。(可能的情况是,如果再次到达该条件,它将被判断为假,但这与判断为假不同。) - chepner

140
else子句在正常退出块时执行,比如命中循环条件或着从try块底部跳出。如果你使用breakreturn跳出块,或者引发异常,则不会执行该子句。它适用于不仅限于while和for循环,还适用于try块。
通常情况下,你会在通常情况下提前退出循环的地方找到else子句,而在循环结束时跳出循环是意外或者不寻常的情况。例如,如果你正在遍历一个列表寻找一个值:
for value in values:
    if value == 5:
        print "Found it!"
        break
else:
    print "Nowhere to be found. :-("

5
这是一个相当有用的结构,经常在循环开头设置found_it=False,并在最后通过对found_it进行if检查来确定是否找到。 - Cruncher
这是一个比上面那个更好的解释,上面的解释是说,“当你的 while 条件变为 false 时,else 子句才会被执行”。如果你的 while 条件变为 false,但在循环之前调用了 "break",那么 else 子句将不会被调用。仅仅将 while 条件变为 false 是不够的,你还必须执行该条件。 - undefined

43

让我举个例子说明为什么要使用这个else 从而更好地解释我的观点,但是:

  • 现在通过Leo的答案更好地解释了我的观点。
  • 我使用for循环而不是while循环,但else的作用类似(只有在没有遇到break时才会执行)
  • 还有更好的方法来实现这一点(例如将其包装成一个函数或引发异常)
跳出多层循环

具体操作如下:外部循环在末尾有一个break语句,所以它只会执行一次。然而,如果内部循环完成(未找到除数),则会到达else语句,外部的break就不会被触发。这样,内部循环中的break将会同时结束两个循环,而不仅仅是一个。

for k in [2, 3, 5, 7, 11, 13, 17, 25]:
    for m in range(2, 10):
        if k == m:
            continue
        print 'trying %s %% %s' % (k, m)
        if k % m == 0:
            print 'found a divisor: %d %% %d; breaking out of loop' % (k, m)
            break
    else:
        continue
    print 'breaking another level of loop'
    break
else:
    print 'no divisor could be found!'

30
当while条件求值为false时,执行else子句。 来自文档

只要表达式为真,while语句就用于重复执行:

while_stmt ::=  "while" expression ":" suite
                ["else" ":" suite]

这段代码反复测试表达式,如果为真,则执行第一个代码块;如果表达式为假(可能是第一次被测试),则执行else子句中的代码块(如果有),并且循环终止。

在第一个代码块中执行的break语句会终止循环而不执行else子句的代码块。在第一个代码块中执行的continue语句将跳过其余代码块,回去测试表达式。


25

while条件变为false时,才会执行else子句。

以下是一些例子:

例1:初始条件为false,所以执行else子句

i = 99999999

while i < 5:
    print(i)
    i += 1
else:
    print('this')

输出:

this

示例2: while条件 i < 5 从未变为false,因为i == 3 打破了循环,所以else子句没有执行。

i = 0

while i < 5:
    print(i)
    if i == 3:
        break
    i += 1
else:
    print('this')

输出:

0
1
2
3

例子 3:i5 时,while-condition i < 5 变为假,因此执行了 else-clause

i = 0

while i < 5:
    print(i)
    i += 1
else:
    print('this')

输出:

0
1
2
3
4
this

23

我的回答将重点关注在何时可以使用while/for-else。

一开始看起来,使用while/for-else好像没有什么不同之处。

while CONDITION:
    EXPRESSIONS
print 'ELSE'
print 'The next statement'
and
while CONDITION:
    EXPRESSIONS
else:
    print 'ELSE'
print 'The next statement'

因为无论是否运行while循环(both when the while loop finished or not run),语句print 'ELSE'似乎总是被执行。

只有当在while代码块中存在break时,才不会执行语句print 'ELSE'

In [17]: i = 0

In [18]: while i < 5:
    print i
    if i == 2:
        break
    i = i +1
else:
    print 'ELSE'
print 'The next statement'
   ....:
0
1
2
The next statement

如果与众不同:

In [19]: i = 0

In [20]: while i < 5:
    print i
    if i == 2:
        break
    i = i +1
print 'ELSE'
print 'The next statement'
   ....:
0
1
2
ELSE
The next statement

return不在这个范畴中,因为它对两种以上的情况产生相同的效果。

异常抛出也没有差别,因为当它被抛出时,下一行将执行的代码位于异常处理程序(except块)中,else子句或者while子句后面的代码都不会被执行。


13

我知道这是个老问题,但是……

正如Raymond Hettinger所说,它应该被称为while/no_break而不是while/else
如果你看一下这个片段,我觉得很容易理解。

n = 5
while n > 0:
    print n
    n -= 1
    if n == 2:
        break
if n == 0:
    print n

现在我们可以将检查条件的位置从 while 循环后交换至 else 语句中,并且去掉该检查。

n = 5
while n > 0:
    print n
    n -= 1
    if n == 2:
        break
else:  # read it as "no_break"
    print n

我通常把它读作while/no_break来理解代码,这种语法对我来说更有意义。


2
thing = 'hay'
while thing:
  if thing == 'needle':
    print('I found it!!')  # wrap up for break
    break
  thing = haystack.next()
else:
  print('I did not find it.')  # wrap up for no-break

可能不幸命名的"else"子句是你在循环耗尽时进行总结的地方,而无需使用break。
- 如果在这种情况下没有任何操作要执行,则不需要"else"子句。 - 你可以使用"return"或"raise"来中断,整个调用或"try"后面的代码将成为你的非中断位置。 - 在"while"之前设置一个默认值(例如"found = False")代替"else" - 但这容易引发与默认值处理相关的错误 - "else"子句可以避免这些错误
如果你在多个中断中有复杂的总结操作,你应该在中断之前使用简单赋值,使用"else"子句进行非中断赋值,并使用"if-elif-else"或"match-case"来避免重复非中断处理代码。
请注意:上述内容同样适用于for thing in haystack:

2

如果while循环没有中断,那么就会执行Else。

我有点喜欢用“赛跑者”的隐喻来思考。

“else”就像是穿过终点线,无论你是从起跑线还是终点线开始的。只有在你中途某处中断时,“else”才不会被执行。

runner_at = 0 # or 10 makes no difference, if unlucky_sector is not 0-10
unlucky_sector = 6
while runner_at < 10:
    print("Runner at: ", runner_at)
    if runner_at == unlucky_sector:
        print("Runner fell and broke his foot. Will not reach finish.")
        break
    runner_at += 1
else:
    print("Runner has finished the race!") # Not executed if runner broke his foot.

主要用例是在嵌套循环中使用此功能,或者如果您只想在循环没有中断的情况下运行某些语句(考虑到中断是一种不寻常的情况)。
例如,以下是一种机制,可以在不使用变量或try/catch的情况下退出内部循环:
for i in [1,2,3]:
    for j in ['a', 'unlucky', 'c']:
        print(i, j)
        if j == 'unlucky':
            break
    else: 
        continue  # Only executed if inner loop didn't break.
    break         # This is only reached if inner loop 'breaked' out since continue didn't run. 

print("Finished")
# 1 a
# 1 b
# Finished

-1
假设你需要在单链表中搜索元素x。
    def search(self, x):
        position = 1
        p =self.start
        while p is not None:
            if p.info == x:
                print(x, " is at position ", position)
                return True
            position += 1
            p = p.link
        else:
            print(x, "not found in list") 
            return False

所以如果 while 条件失败,else 将会执行,希望这能帮到你!


在这段代码中省略while语句的else:子句并不会改变代码的行为。 - sleighty
@BrunoEly 这段代码是单链表程序的一部分,因此您需要整个代码才能准确运行它。 - Rahul Negi
3
你误解了我的观点。我想说的是,在一个没有break语句的while块后面添加else:子句是毫无意义的,它不会改变代码的行为。 - sleighty
是的,我明白了,我不知道我怎么会误解那个时间(我的错)。 即使有break语句,else也不会执行。 - Rahul Negi

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