我理解这种结构是如何运作的:

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和continuouswith更有意义(我正试着训练自己这样读它)。

我想知道Python程序员是如何在头脑中(或者大声地,如果你喜欢)阅读这个结构的。也许我遗漏了一些可以使这些代码块更容易破译的东西?


这个问题是关于底层设计决策的,也就是说,为什么能够编写这些代码是有用的。另请参阅Python while语句中的Else子句,了解语法含义的具体问题。


当前回答

The easiest way I found to 'get' what the for/else did, and more importantly, when to use it, was to concentrate on where the break statement jumps to. The For/else construct is a single block. The break jumps out of the block, and so jumps 'over' the else clause. If the contents of the else clause simply followed the for clause, it would never be jumped over, and so the equivalent logic would have to be provided by putting it in an if. This has been said before, but not quite in these words, so it may help somebody else. Try running the following code fragment. I'm wholeheartedly in favour of the 'no break' comment for clarity.

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

编辑-我注意到这个问题仍然在运行

第二个更好的想法……

“没有休息”的评论是负面的。要理解一个正的断言要容易得多,那就是for可迭代对象已经用完了。

for a in range(3):
    print(a)
    if a==4: # change value to force break or not
        print('ending for loop with a break')
        break
else: # for iterable exhausted  
    print('ending for loop as iterable exhausted')

print('for loop ended one way or another')

这也强化了这种解释

if iterable_supplies_a_value:
    run_the_for_with_that_value
else:
    do_something_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()

else子句在循环正常完成后执行。这意味着The:==> 只有当循环没有被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 ")

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

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

使用Python进行…你的其他构念

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循环紧密绑定。第二种情况是,绑定不那么牢固,在维护过程中可能会出现错误。

即使对经验丰富的Python程序员来说,这也是一个奇怪的结构。当与for-loops结合使用时,它的基本意思是“在可迭代对象中找到某个项,否则如果没有找到则do…”。如:

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…其他线程

我把它读成这样:“当可迭代对象完全耗尽时,在完成for语句后,执行即将继续执行下一个语句,else子句将被执行。”因此,当迭代被break打断时,这将不会执行。