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

时间:2012-04-02 16:18:46

标签: python if-statement for-loop for-else

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

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

23 个答案:

答案 0 :(得分:442)

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

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

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

答案 1 :(得分:217)

即使对经验丰富的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

答案 2 :(得分:140)

Raymond Hettinger的精彩演讲题为Transforming Code into Beautiful, Idiomatic Python,其中简要介绍了for ... else构造的历史。相关部分是"区分循环中的多个退出点" starting at 15:50并持续约三分钟。以下是要点:

  • Donald Knuth设计的for ... else构造取代了某些GOTO用例;
  • 重复使用else关键字是因为"它是Knuth所使用的,而且人们知道,当时所有[for语句都嵌入了{{1}下面是{}和if,他们期待GOTO;"
  • 事后看来,它应该被称为"没有休息" (或者可能" nobreak"),然后它不会令人困惑。*

所以,如果问题是,"为什么他们不改变这个关键字?"那么Cat Plus Plus probably gave the most accurate answer - 在这一点上,它对现有的代码来说太具有破坏性了。但是,如果您真正问的问题是为什么else首先被重复使用,那么,显然当时这似乎是一个好主意。

就我个人而言,我喜欢在else内线评论的妥协,只要# no break可能会被误认为属于循环内部。它相当清晰简洁。在他的回答结束时,the summary that Bjorn linked会简要提及此选项:

  

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

else

*来自视频部分的奖金引用:"就像我们叫lambda makefunction一样,没有人会问,' lambda做什么?&# 39;"

答案 3 :(得分:31)

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

答案 4 :(得分:16)

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

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

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

答案 5 :(得分:15)

我发现'获得'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')

答案 6 :(得分:14)

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

  

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

来源:Python 2 docs: Tutorial on control flow

答案 7 :(得分:12)

我读了类似的东西:

如果仍然在运行循环的条件下,请执行操作, else 执行其他操作。

答案 8 :(得分:7)

由于技术部分得到了很好的回答,我的评论与产生回收关键字的混淆有关。

Python是一种非常雄辩的编程语言,滥用关键字更加臭名昭着。 else关键字完美地描述了决策树流程的一部分,"如果您不能这样做,(否则)执行该操作"。它用我们自己的语言隐含

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

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

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

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

它类似于一些孩子在组装玩具后的每一步都有的情况:那么爸爸是什么?

答案 9 :(得分:5)

我把它读成“当iterable完全耗尽,并且在完成for后执行即将进入下一个语句时,将执行else子句。”因此,当迭代被break打破时,这将不会被执行。

答案 10 :(得分:4)

我同意,这更像是'elif不是[条件提升休息]。

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

对我来说,有三种方法可以“阅读”elseFor... else语句中的While... 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)(大概有这种情况,或者你不会有循环)
  4. 所以,基本上,循环中的“else”实际上是“elif ...”,其中'...'是(1)没有中断,这相当于(2)NOT [condition( s)提高休息时间。

    我认为关键是else在没有“休息”的情况下毫无意义,因此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:变为“如果找不到目标项目该怎么办”。

    示例

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

答案 11 :(得分:4)

此处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

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

答案 12 :(得分:3)

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

  

循环语句可能有一个else子句;当循环通过列表耗尽(with for)或条件变为false(with 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语句和异常的更多信息,请参阅处理异常。

     

continue语句也是从C借用的,继续循环的下一次迭代:

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

答案 13 :(得分:2)

你可以想到它, else和剩下的东西一样,或者其他东西,在循环中没有完成。

答案 14 :(得分:2)

这是一种我上面没有见过其他人提到的方式:

首先,请记住,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完全相同,具有附加的循环功能,直到条件变为假,而不是仅检查条件一次。

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

答案 15 :(得分:1)

这是除搜索之外的另一个惯用用例。让我们说你想等待条件成立,例如要在远程服务器上打开的端口,以及一些超时。然后你可以像这样使用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() 结构:

SgmlLinkExtractor

答案 16 :(得分:1)

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本质上是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

答案 17 :(得分:1)

好的答案是:

  • this解释了历史,以及
  • this赋予权利 引用以简化您的翻译/理解。

这里的注释来自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的语法糖,而while-else在其break块的末尾具有隐式if。相反的含义是while循环是if的扩展,这是更常见的(它只是重复/循环的条件检查),因为if通常在while之前被教。但是,这是不正确的,因为这将意味着在条件为false时,每次每次都会执行while-else中的else块。

为便于理解,您应该这样考虑:

  

没有breakreturn等,循环仅在条件不再为真时结束,并且在这种情况下,else块也将执行一次。如果使用Python for,则必须考虑使用C样式的for循环(有条件)或将其转换为while

另一个说明:

  

过早的breakreturn等内部循环使条件不可能变为假,因为在条件为真时执行会跳出循环,并且再也不会再次检查它。 / p>

答案 18 :(得分:1)

<块引用>

我想知道 Python 编码人员如何在脑海中(或大声朗读,如果您愿意)阅读此结构。

我只是在脑子里想:

<块引用>

“否则没有遇到break...”

就是这样!

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

参考:

请参阅此处:https://book.pythontips.com/en/latest/for_-_else.html#else-clause(添加了强调,并将“not”更改为“NOT”):

<块引用>

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


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

答案 19 :(得分:0)

我只是想自己重新理解。我发现以下帮助!

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

答案 20 :(得分:0)

Python在for和while循环之后使用else,因此如果没有任何作用于该循环,则会发生其他事情。例如:

test = 3
while test == 4:
     print("Hello")
else:
     print("Hi")

输出将是一遍又一遍的“嗨”(如果我没错的话)。

答案 21 :(得分:0)

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 将完成剩下的部分。

答案 22 :(得分:-1)

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

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

可以将

for(if)视为特殊的if,它会循环执行以尝试满足if条件。一旦满足 if 条件,A和 break 其他,B。