关于if语句:为什么python在for和while循环之后使用”else”?

Why does python use 'else' after for and while loops?

我了解这种结构的工作原理:

1
2
3
4
5
6
7
8
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块。对我来说,continuecontinuewith更有意义(我正努力训练自己去读)。

我想知道Python编码人员是如何在他们的头脑中阅读这个结构的(或者大声朗读,如果您愿意的话)。也许我遗漏了一些可以使这样的代码块更容易识别的东西?


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

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

使用python forelse构造

1
2
3
4
5
6
for i in mylist:
    if i == theflag:
        break
    process(i)
else:
    raise ValueError("List argument missing terminal flag.")

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

1
2
3
4
5
6
7
8
9
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编码人员来说,这也是一个奇怪的构造。当与for循环结合使用时,它基本上意味着"在iterable中查找某些项,否则如果没有找到则执行…"。如:

1
2
3
4
5
6
7
found_obj = None
for obj in objects:
    if obj.key == search_key:
        found_obj = obj
        break
else:
    print('No object found.')

但是,只要您看到这个构造,一个更好的选择就是将搜索封装在一个函数中:

1
2
3
4
def find_obj(search_key):
    for obj in objects:
        if obj.key == search_key:
            return obj

或者使用列表理解:

1
2
3
4
5
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块。

另请参见摘要了解其他线程


Raymond Hettinger的一篇精彩的演讲题为"将代码转换成漂亮的、惯用的Python",其中他简要介绍了for ... else构造的历史。相关章节是"区分回路中的多个出口点",从15:50开始,持续约三分钟。以下是要点:

  • Donald Knuth设计了for ... else结构,作为某些GOTO用例的替换;
  • 重复使用else关键字是有意义的,因为"这是knuth使用的,当时人们知道所有的[for语句]都在下面嵌入了ifGOTO,他们期望else
  • 事后看来,它应该被称为"不间断"(或者可能是"不间断"),然后它就不会令人困惑了。

所以,如果问题是,"他们为什么不更改这个关键字?"然后,cat plus可能给出了最准确的答案——此时,它对现有代码的破坏性太大,不实用。但如果你真正问的问题是,为什么一开始使用else,那么,显然,当时这是个好主意。

就我个人而言,我喜欢在else可能被误认为属于循环内部的地方对# no break进行在线评论。它相当清晰和简洁。这个选项在总结中得到了一个简短的提及,比约恩在回答的最后链接了:

For completeness, I should mention that with a slight change in
syntax, programmers who want this syntax can have it right now:

1
2
3
4
for item in sequence:
    process(item)
else:  # no break
    suite

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


因为他们不想给语言引入新的关键字。每一个都会窃取一个标识符并导致向后兼容性问题,所以这通常是最后的解决办法。


我发现"得到"for/else所做的,更重要的是,何时使用它,最简单的方法是集中精力在break语句跳到哪里。for/else构造是单个块。break跳出了块,因此跳过了else子句。如果else子句的内容只跟在for子句之后,则不会跳过它,因此必须通过将其放入if来提供等效逻辑。这句话以前说过,但用这些话来说不完全是,所以它可能对其他人有所帮助。尝试运行以下代码片段。我全心全意地支持"不间断"的评论,以便澄清。

1
2
3
4
5
6
7
8
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')


我认为文档对其他方面有很好的解释,继续

[...] it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement."

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


我读到的内容有:

如果仍然在运行循环的条件下,做一些事情,或者做一些其他的事情。


简单点说,你可以这样想;

  • 如果在for循环中遇到break命令,则不会调用else部分。
  • 如果在for循环中没有遇到break命令,将调用else部分。

换句话说,如果for循环迭代没有与break一起"中断",那么将调用else部分。


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

由于python是一种非常雄辩的编程语言,关键字的误用更加臭名昭著。else关键字完美地描述了决策树流的一部分,"如果您不能这样做,(否则)就这样做"。它用我们自己的语言表达。

相反,在whilefor语句中使用这个关键字会造成混乱。原因是,我们作为程序员的职业生涯告诉我们,else语句位于决策树中;它的逻辑范围是一个包装器,它有条件地返回一条要遵循的路径。同时,循环语句有一个形象化的明确目标来实现某些东西。目标是在一个过程的连续迭代之后实现的。

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

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

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

它类似于某个孩子在组装玩具的每一步之后所遇到的情况:然后是什么爸爸?


我把它读成"当iterable完全耗尽,并且在完成for之后执行到下一个语句时,就会执行else子句。"这样,当迭代被break中断时,就不会执行。


我同意,这更像是一个'伊利夫不是[条件提高休息''。

我知道这是一条古老的线索,但我现在正在研究同一个问题,我不确定是否有人以我理解的方式抓住了这个问题的答案。

对我来说,有三种方法可以"阅读"For... elseWhile... else中的else,它们都是等价的:

  • else==if the loop completes normally (without a break or error)
  • else==if the loop does not encounter a break
  • else==else not (condition raising break)(可能有这种情况,或者你不会有循环)
  • 因此,本质上,循环中的"else"实际上是一个"elif…",其中"…"是(1)no break,相当于(2)not[条件引发break]。

    我认为关键是else没有"中断"是没有意义的,所以for...else包括:

    1
    2
    3
    4
    5
    for:
        do stuff
        conditional break # implied by else
    else not break:
        do more stuff

    因此,for...else循环的基本元素如下,您可以用简单的英语将其读作:

    1
    2
    3
    4
    5
    6
    for:
        do stuff
        condition:
            break
    else: # read as"else not break" or"else not condition"
        do more stuff

    正如其他海报所说,当你能找到你的循环所要查找的内容时,通常会出现中断,所以else:变成"如果目标项目不在,该怎么做"。

    例子

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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")

    结果

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

    例子

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    for y in range(0,3):
        print("y: {}".format(y))
        if y == 2: # will be executed
            print("BREAK: y is {}
    ----------"
    .format(y))
            break
    else: # not executed because break is hit
        print("y_loop completed without break----------
    "
    )

    结果

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

    例子

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    for z in range(0,3):
         print("z: {}".format(z))
         if z == 4: # will not be executed
             print("BREAK: z is {}
    "
    .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
    ----------
    "
    )

    结果

    1
    2
    3
    4
    5
    z: 0
    z: 1
    z: 2
    z_loop complete without break or error
    ----------

    在这里,else关键字可能会混淆,正如许多人所指出的,像nobreaknotbreak这样的关键字更合适。

    为了从逻辑上理解for ... else ...,将它与try...except...else,而不是if...else...,大多数python程序员都熟悉以下代码:

    1
    2
    3
    4
    5
    6
    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

    1
    2
    3
    4
    5
    6
    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不能写出来,所以它变成:

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

    是的,我知道这种比较可能很困难也很烦人,但它确实澄清了这种混淆。


    for循环没有中断时,执行else语句块中的代码。

    1
    2
    3
    4
    5
    6
    7
    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子句

    Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement. This is exemplified by the following loop, which searches for prime numbers:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    >>> 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

    (Yes, this is the correct code. Look closely: the else clause belongs to the for loop, not the if statement.)

    When used with a loop, the else clause has more in common with the else clause of a try statement than it does that of if statements: a try statement’s else clause runs when no exception occurs, and a loop’s else clause runs when no break occurs. For more on the try statement and exceptions, see Handling Exceptions.

    The continue statement, also borrowed from C, continues with the next iteration of the loop:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    >>> 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


    以下是一种思考方法,我没有看到其他人提到过:

    首先,记住for循环基本上只是在循环的同时加上句法上的糖分。例如,循环

    1
    2
    for item in sequence:
        do_something(item)

    可以重写(大约)为

    1
    2
    3
    4
    item = None
    while sequence.hasnext():
        item = sequence.next()
        do_something(item)

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

    因此,尽管/else有着完美的意义:它的结构与if/else完全相同,具有循环直到条件变为假的附加功能,而不是只检查条件一次。

    然后for/else也有很好的意义:因为all for循环只是while循环之上的句法糖分,所以您只需要弄清楚while循环的隐式条件是什么,然后当条件变为false时,其他条件就相应了。


    你可以这样想,像其他的东西,或者其他的东西一样,这些不是在循环中完成的。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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()

    1
    2
    3
    4
    5
    6
    7
    8
    for i in range(3):
        print(i)

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

    这里的"否则"非常简单,只是意味着

    1,"如果for clause完成"

    1
    2
    3
    4
    5
    6
    7
    8
    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"。

    这里有一个if的性质。

    不过,埃多克斯1〔23〕怎么样?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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

    所以完全是逻辑组合:

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

    或者这样说:

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

    或者这样:

    1
    2
    if"for clause not encounter a break":
        do else stuff


    好的答案是:

    • 这解释了历史,以及
    • 这就是权利引用以便于您的翻译/理解。

    这里我的注释来自唐纳德·克努斯(Donald Knuth)曾经说过的(抱歉找不到引用),即存在一个构造,其中while else与if else不可区分,即(在python中):

    1
    2
    3
    4
    5
    6
    x = 2
    while x > 3:
        print("foo")
        break
    else:
        print("boo")

    具有与以下相同的流量(不包括低液位差):

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

    重点是,如果else可以被看作是语法糖,而else在其if块末尾具有隐式中断。相反的含义是,while循环是if循环的扩展,因为它只是重复的条件检查更常见。然而,当您考虑是否存在其他情况时,它就显得不足了,因为这意味着每次条件为假时,都会执行else块,而else。

    为了减轻你的理解,请这样想:

    Without break, return, etc., loop ends only when condition is no longer true (in for case you must consider C-style for loops or translate them to while) and else block executes when condition is false.

    另一个注意事项:

    Premature break, return, etc. inside loop makes impossible for condition to become false because execution jumped out of the loop while condition was true and would never come back to check it again.


    我只是想自己再弄明白一次。我发现以下有帮助!

    ? ;将else想象为与循环内的if配对(而不是与for)——如果满足条件,则断开循环,否则执行此操作——除非它是一个else与多个if配对!?&如果根本不满足if,那么执行else。?&多个ifs实际上也可以被认为是ifelifs!


    假设我们有一个函数

    1
    def broken(x) : return False if x==5 else True

    也就是说只有5个没有坏。现在,如果未使用5对损坏进行评估:

    1
    2
    3
    4
    for x in range(4):
        if not broken(x) : break
    else:
        print("Everything broken... doom is upon us")

    将给出输出:

    1
    Everything broken... doom is upon us

    其中,当破碎时用5:

    1
    2
    3
    4
    for x in range(6):
        if not broken(x) : break
    else:
        print("Everything broken... doom is upon us")

    它不会打印任何东西。因此,间接地告诉至少有一些东西是不坏的。

    但是,如果你想欺骗和跳过你发现的坏东西。也就是说,即使您发现5已损坏,仍继续循环,否则语句仍将被打印。即:

    1
    2
    3
    4
    for x in range(6):
        if not broken(x) : continue
    else:
        print("Everything broken... doom is upon us")

    将打印

    1
    Everything broken... doom is upon us

    我希望它能消除混乱,而不是创造一个新的混乱。


    我把这个结构看成是(如果)A或者B,(如果)Else大致上是一个特殊的if-Else。这可能有助于理解其他。

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

    for(if)可以被视为一个特殊的if,它执行一个循环来尝试满足if条件。一旦满足if条件,a和break;否则,b。