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


480

我了解此构造的工作原理:

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还是continuewith更有意义(我正在尝试训练自己这样阅读)。

我想知道Python编码人员是如何在头脑中读取这个结构的(如果愿意,可以大声读出)。也许我缺少使这些代码块更容易理解的东西?


26
您可能想将其翻译为“ then”。
Marcin 2012年

63
不要忘记Python Zen中的关键点:“ ...除非您是荷兰人,否则这种方式一开始可能并不明显。”
Daniel Roseman

50
在我的脑海中,我将其翻译为“如果不中断”。而且,由于break“我已经找到它”循环中使用很多,因此您可以将其翻译为“如果没有找到”,这与所else读内容相距不远
MestreLion

29
我认为很多人真正的问题是“ for ... else foo()foo()for循环之间有什么区别?” 答案是,当循环包含a时,它们的行为才会不同break(如下所述)。
山姆考夫曼2014年

10
python中的分号...我的眼睛受伤..即使在语法上正确,这样做也不是好习惯
DarkCygnus

Answers:


278

即使是经验丰富的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-思想] for ... else线程的摘要


50
列表理解是错误的一句话。如果要在for循环示例中查找单个项目,并且想要使用生成器表达式/列表推导,则需要next((o for o in objects if o.key == search_key), None)将其包装在try/中,except并且不使用默认值代替if/ else
2012年

4
和Lance Helsten的答案一样,在实际情况下,最好使用for/else构造。
andrean 2014年

5
干杯。我有一个严重缩进的文件,其中一个else与配对,for我不知道那是合法的。
maxywb 2014年

3
我认为for循环是那里最明显的结构。
Miles Rout 2014年

14
值得一提的是,即使for循环具有值,否则else子句也将运行,除非break在此示例中显式运行一条语句。从上面的文档中可以看出:“该else子句还有另一个可感知的问题:如果break循环中没有else子句,则该子句在功能上是多余的。” 例如for x in [1, 2, 3]:\n print x\n else:\n print 'this executes due to no break'
dhackner 2014年

585

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

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

使用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循环。第二,绑定不那么牢固,并且在维护期间可能会引入错误。


69
这比选择的答案要好,因为在选择答案时,作者并没有真正了解其他内容!
erikbwork 2014年

17
我不得不说这种语法糖可能会腐蚀您的项目。这不会做Python: the good parts书。
boatcoder

1
您能否确认在您的示例中,process(i)每一项都mylist严格在之前发生theflag,而不是在theflag本身发生?这是原意吗?
bli 2015年

4
processi在到达列表之前存在于列表中的每个theflag元素上执行,不会在列表之后存在于元素上执行theflag,也不会在上执行theflag
Lance Helsten

1
else语句也被执行,如果迭代没有任何元素
失落的小钩

173

Raymond Hettinger的精彩演讲名为“ 将代码转换为美丽的惯用Python”,在其中他简要介绍了该for ... else构造的历史。相关部分是“在循环中区分多个出口点”,从15:50开始,持续大约三分钟。这里是要点:

  • for ... else结构是由Donald Knuth设计的,用于替换某些GOTO用例。
  • 重用该else关键字是有道理的,因为“这是Knuth所使用的,那时人们知道,所有[ for语句]都嵌入了an ifGOTOunder,而他们期望使用else;”。
  • 事后看来,它应该被称为“不间断”(或可能称为“不间断”),这样就不会造成混淆。*

因此,如果问题是“他们为什么不更改此关键字?” 那么Cat Plus Plus可能给出了最准确的答案 –在这一点上,它对现有代码的破坏性太大,无法实用。但是,如果您真正要问的问题是为什么else首先要重用,那么显然在当时看来是个好主意。

就个人而言,我喜欢# no break在线注释的妥协之else处,因为它们一眼就可能被误认为属于循环内。相当清晰简洁。Bjorn在回答结束时链接的摘要中简要提及了该选项:

为了完整起见,我应该提到的是,语法稍有变化,想要这种语法的程序员现在可以使用它:

for item in sequence:
    process(item)
else:  # no break
    suite

*视频那部分的奖励语录:“就像我们调用lambda makefunction一样,没人会问'lambda做什么?'”


33

因为他们不想在语言中引入新的关键字。每个人都窃取一个标识符并引起向后兼容性问题,因此通常是最后的选择。


2
finally在这种情况下,似乎是更好的选择。在引入此结构时,final关键字还不存在吗?
Ponkadoodle

26
@Wallacoloo finally并没有更好,因为它意味着该块将始终在循环之后执行,而实际上并非如此(因为仅将代码放在循环之后运行是多余的)。
Cat Plus Plus

也可能不是finally因为continue在for循环中使用else子句时也会执行else子句-这可能很多次,而不仅仅是在结尾。
pepr 2014年

6
@pepr else子句执行不受continue文档测试代码
空气

@AirThomas:+1。你是对的。在else只有当执行continue是一个最后一次迭代。
pepr 2014年

33

简单起见,您可以这样想:

  • 如果breakfor循环中遇到命令,else则不会调用该部件。
  • 如果breakfor循环中未遇到该命令,else则将调用该部件。

换句话说,如果for循环迭代未被破坏breakelse则将调用该部分。


else如果循环的主体引发异常,则也不会执行该块。
Amal K

17

我发现“了解” 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')

“中断跳出了块,因此跳过了else子句” -虽然这可能是“获取” for:/的一种方式else:,但实际上并不能为关键字being提供理由else。考虑到这里给出的框架,then:看起来会更加自然。(这里原因else在其他的答案给出的被选择, -他们在这里并没有提供。)
马克·阿梅里奥

15

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

[...]当循环通过用尽列表而终止(使用for)或条件变为假(使用while)时执行,但在循环由break语句终止时则不执行。”

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


13

我读到类似:

如果仍然有运行循环的条件,则执行其他操作,否则执行其他操作。


的状况仍然有所帮助(+1),尽管它是错误的-这是人为的;-)
Wolf

-1; for:/的发音else:使它听起来else:总是会在循环之后运行,事实并非如此。
Mark Amery

11

由于已经回答了很多技术方面的问题,因此我的评论仅与产生此回收关键字的混乱有关。

作为Python是一种非常有说服力的编程语言,关键字的滥用更为臭名昭著。该else关键字恰如其分地描述决策树的流程的一部分,“如果你不能做到这一点,(否则)做到这一点。” 它是用我们自己的语言暗示的

相反,将此关键字与whileand for语句一起使用会引起混淆。原因是,我们作为程序员的职业使我们知道该else语句位于决策树之内。它的逻辑范围,一个有条件地返回要遵循的路径的包装器。同时,循环语句具有比喻明确的目标。在流程的不断迭代之后,可以达到目标。

if / else 指明可以遵循的道路。循环遵循一条路径,直到“目标”完成

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

最后,关键字将保留在Python中。显然,这是错误的,当每个程序员尝试提出一个故事来理解其用法(如某种助记符设备)时,这一点就更加清楚。如果他们选择了关键字,我会很喜欢的then。我相信这个关键字非常适合迭代流程,即循环后的收益

这类似于某些孩子在按照每个步骤组装玩具后所遇到的情况:那么,爸爸是什么?


我认为这个答案解决了我认为OP正在谈论的混乱问题。当附加到for的动作时,else关键字与else的英语含义完全相反。从理论上讲,for ... else的工作方式可能有所不同,因为当循环中断时,您最终会处于else部分,但是问题是要使用它来查找元素x,并处理x为找不到,您可能必须在整体后使用标志或其他测试来构建.. else结构
Spacen Jasset

7

我将其读为“当iterable完全耗尽时,执行将在完成完成后继续执行下for一条语句,否则将执行else子句。” 因此,当迭代被中断时break,将不会执行。


6

我同意,它更像是“不是[[条件]打破休息条件]”。

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

对我来说,有三种“读取” elsein For... elseWhile... 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) (大概有这种情况,否则您将不会循环)

因此,从本质上讲,循环中的“ else”实际上是一个“ elif ...”,其中“ ...”是(1)不间断,相当于(2)NOT [引起中断的条件]。

我认为关键是else没有'break'就没有意义,因此a 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
----------

6

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

是的,我知道这种比较可能很困难并且很累,但是确实可以澄清这种混淆。


从资源复制时,您应该链接到资源:Nick Coghlan的Python Notes
godaygo

@godaygo感谢您的链接。我在初次学习python时就阅读并接受了这一概念,在编写答案时并没有记住源代码。
cizixs

@cizixs您“没有记住源”,只是碰巧包括了与原始注释相同的整个句子?哎呀
Mark Amery

5

elsefor不中断循环时,将执行语句块中的代码。

for x in xrange(1,5):
    if x == 5:
        print 'find 5'
        break
else:
    print 'can not find 5!'
#can not find 5!

文档:中断并继续执行语句,否则循环中的子句

循环语句可以包含else子句;当循环通过用尽列表而终止(使用for)或条件变为假(使用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语句和异常的更多信息,请参见处理异常。

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

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

1
这增加了什么,不回答这个问题,这是不怎样,但为什么
航空

5

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

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

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


4

很好的答案是:

  • 可以解释历史,并且
  • 这样可以正确引用,以简化您的翻译/理解。

我在这里的注释来自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时else都会执行while-else块。

为了简化您的理解,请考虑以下方式:

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

另一个注意事项:

过早breakreturn等内部循环,使不可能的条件,成为虚假的,因为执行跃升循环出来,而条件是真实的,它永远不会回来再检查一遍。


3

您可以将其想像为 else其余内容或其他内容中未在循环中完成的。


3
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 这本质上是一种假设。

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

其他在SQL中充当“事务”。
微积分

2

除了搜索之外,这是另一个惯用例。假设您要等待条件为真,例如,要在远程服务器上打开端口以及一些超时。然后,您可以利用这样的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()

1

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

•将else视为与if循环内部配对(而不是与for)配对-如果满足条件,则打破循环,否则执行此操作-除非它else与多个ifs 配对!
•如果根本if不满意,请执行else
•多重ifS可实际上也被认为是if- elifs ^!


-2

我认为结构为(如果)在其他B,以及(如果)-else是一个特殊的if-else粗略。可能有助于了解其他

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

for(if)可以认为是特殊的if,它会循环执行以尝试满足if条件。一旦满足if条件,则A 中断否则,B.


-2

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

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

输出将是一遍又一遍的“ Hi”(如果我是对的)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.