首页 文章

为什么python在for和while循环之后使用'else'?

提问于
浏览
302

我理解这个结构是如何工作的:

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编码器如何在他们的头脑中读取这个结构(或者如果你愿意的话,大声朗读) . 也许我错过了一些会使这些代码块更容易破译的东西?

19 回答

  • 184

    我发现“获取”for / else所做的最简单的方法,更重要的是,什么时候使用它,是专注于break语句跳转到的位置 . For / else构造是一个单独的块 . 休息跳出块,因此跳过'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')
    
  • 117

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

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

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

    换句话说,如果for循环迭代不是"broken"且 break ,则将调用 else 部分 .

    这很简单 .

  • 0
    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 完成”

    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 clause is completed”这样长的陈述,因此他们引入了“else” .

    else 这是一个if的性质 .

    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
    
  • 2

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

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

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

  • 13

    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!
    

    来自docs: break and continue Statements, and else Clauses on Loops

    Loop语句可能有一个else子句;当循环通过列表的耗尽(with for)或条件变为false(with while)时终止,但是当循环被break语句终止时,它被执行 . 这通过以下循环来举例说明,该循环搜索素数:>>>对于范围内的n(2,10):
    ...对于范围内的x(2,n):
    ...如果n%x == 0:
    ... print(n,'equals',x,'*',n // x)
    ......休息
    ......别的:
    ......#循环没有找到一个因素
    ... print(n,'是素数')
    ...
    2是素数
    3是素数
    4等于2 * 2
    5是素数
    6等于2 * 3
    7是素数
    8等于2 * 4
    9等于3 * 3
    (是的,这是正确的代码 . 仔细看看:else子句属于for循环,而不是if语句 . )当与循环一起使用时,else子句与try语句的else子句有更多共同之处执行if语句:try语句的else子句在没有异常发生时运行,并且循环的else子句在没有发生中断时运行 . 有关try语句和异常的更多信息,请参阅处理异常 . continue语句也是从C借用的,继续循环的下一次迭代:>>> for num in range(2,10):
    ...如果num%2 == 0:
    ...打印(“找到一个偶数”,num)
    ......继续
    ...打印(“找到一个数字”,num)
    找到了偶数2
    找到了3号
    找到了一个偶数4
    找到了5号
    找到了一个偶数6
    找到了7号
    找到了偶数8
    找到了9号

  • 28

    我看了它的样子:

    如果仍然在运行循环的条件,做的东西,否则做其他事情 .

  • 3

    我把它读作“当 iterable 完全耗尽,并且执行即将进行到完成 for 后的下一个语句,将执行else子句 . “因此,当迭代被 break 中断时,这将不会被执行 .

  • 3

    else 关键字在这里可能令人困惑,正如许多人所指出的那样, nobreaknotbreak 之类的东西更合适 .

    为了理解 for ... else ... 逻辑,将它与 try...except...else 进行比较,而不是 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
    

    是的,我知道这种比较可能是困难和令人厌烦的,但它确实澄清了混乱 .

  • 0

    Raymond Hettinger的精彩演讲题为Transforming Code into Beautiful, Idiomatic Python,其中简要介绍了 for ... else 构造的历史 . 相关部分为"Distinguishing multiple exit points in loops" starting at 15:50并持续约三分钟 . 以下是要点:

    • 由Donald Knuth设计的 for ... else 构造取代了某些 GOTO 用例;

    • 重用 else 关键字是有意义的,因为“这是Knuth使用的,人们都知道,当时所有[ for 语句]都嵌入了 ifGOTO ,他们期望 else ;”

    • 事后看来,它应该被称为"no break"(或可能是"nobreak"),然后它就不会令人困惑 . *

    所以,如果问题是"Why don't they change this keyword?"那么Cat Plus Plus probably gave the most accurate answer - 此时,对现有代码来说实际上太具破坏性了 . 但是如果你真正问的问题是为什么 else 首先被重复使用,那么显然当时这似乎是一个好主意 .

    就个人而言,我喜欢在线注释 # no break 的妥协,只要 else 可能会被误认为是属于循环内部 . 它相当清晰简洁 . 在他的回答结尾处,这个选项在the summary that Bjorn linked中得到了简要的提及:

    为了完整性,我应该提一下,语法略有变化,想要这种语法的程序员现在可以拥有它:对于顺序的项目:
    流程(项目)
    否则:#不休息
    套房


    *来自视频部分的奖金引用:“就像我们打电话给lambda makefunction一样,没有人会问,'lambda做什么?'”

  • 367

    您可以将其视为 else ,就像其他内容一样,或其他内容,这些都不是在循环中完成的 .

  • 10

    对于经验丰富的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] Summary of for...else threads

  • 6

    我只是想让自己再次理解它 . 我发现以下有帮助!

    •将 else 视为与循环内的 if 配对(而不是与 for 配对) - 如果条件满足则打破循环,否则执行此操作 - 除了它是 else 与多个 if 配对!
    •如果根本没有满足 if ,则执行 else .
    •多个 if 也可以被认为是 if - elif s!

  • 12

    这里's another idiomatic use case besides searching. Let' s说你想等待条件成立,例如要在远程服务器上打开的端口,以及一些超时 . 然后你可以使用 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()
    
  • 3

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

    [...]当循环通过列表耗尽(with for)或条件变为false(with while)时终止,但是当循环被break语句终止时不执行 . “

    资料来源:Python 2 docs: Tutorial on control flow

  • 0

    I agree, it's more like an 'elif not [condition(s) raising break]'.

    我知道这是一个老话题,但我现在正在研究同样的问题,而且我不确定是否有人以我理解的方式捕捉到了这个问题的答案 .

    对我来说,在 For... elseWhile... else 语句中有"reading"的三种方式,所有这些方法都是等价的,它们是:

    • 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) (可能是有这样的情况,或者你没有循环)

    So, essentially, the "else" in a loop is really an "elif ..." where '...' is (1) no break, which is equivalent to (2) NOT [condition(s) raising break].

    我认为关键是 else 没有'break'是没有意义的,所以 for...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: 变成"what to do if target item not located" .

    Example

    您还可以一起使用异常处理,中断和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
    

    Example

    打破中断的简单示例 .

    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
    

    Example

    简单示例,没有中断,没有条件提出中断,也没有遇到错误 .

    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
    ----------
    
  • 1

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

  • 7

    由于技术部分已得到很好的回答,我的评论只与生成此回收关键字的 confusion 有关 .

    作为一种非常雄辩的编程语言,滥用关键字更加臭名昭着 . else 关键字完美地描述了决策树流的一部分"if you can't do this, (else) do that" . 它隐含在我们自己的语言中 .

    相反,将此关键字与 whilefor 语句一起使用会产生混淆 . 原因是,我们作为程序员的职业生涯告诉我们 else 语句存在于决策树中;它的 logical scope ,一个有条件地返回一条路径的包装器 . 同时,循环语句有一个具象的明确目标来达到某种目的 . 在连续迭代过程之后,目标得以实现 .

    if / else 表示要遵循的路径 . 循环遵循路径,直到"goal"完成 .

    问题是 else 是一个明确定义条件中最后一个选项的词 . 这个词的 semantics 都由Python和Human Language共享 . 但人类语言中的其他词语从未用于表示某事物完成后某人或某物所采取的行动 . 如果在完成它的过程中问题上升(更像是一个中断声明),它将被使用 .

    最后,关键字将保留在Python中 . 如果他们选择了关键字 then ,那么's clear it was mistake, clearer when every programmer tries to come up with a story to understand its usage like some mnemonic device. I'已经爱过了 . 我相信这个关键字非常适合迭代流程,循环后的收益 .

    它类似于一些孩子在完成组装玩具的每一步后所拥有的情况:而且 THEN 爸爸是什么?

  • 0

    这是一种思考方式,我没有看到上面提到过的任何人:

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

    for item in sequence:
        do_something(item)
    

    可以重写(大约)为

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

    其次,请记住while-loops基本上只是重复if-blocks!你总是可以读取一个while循环“如果这个条件成立,执行正文,然后再回来检查” .

    因此,while / else非常有意义:它与if / else完全相同,具有循环的附加功能,直到条件变为false而不是仅检查条件一次 .

    然后for / else也是完全合理的:因为所有for循环只是while循环之上的语法糖,你只需要弄清楚底层while循环的隐式条件是什么,然后else对应于何时条件变为假 .

  • 5

    让我们假设我们有一个功能

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

    这意味着只有5个没有被打破 . 现在,如果 broken 永远不会被评估为5: -

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

    会给出输出: -

    Everything broken... doom is upon us
    

    broken 用5评估时的位置: -

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

    它不会打印任何东西 . 因此,间接地告诉至少有一些东西没有被打破 .

    但是,如果你想欺骗并跳过你发现的东西,那么你就会被打破 . 也就是说,即使您发现5已损坏,继续循环,否则仍将打印其他语句 . 那是 :-

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

    会打印

    Everything broken... doom is upon us
    

    我希望它清除混乱,而不是创建一个新的:-)

相关问题