我怎样才能理解Python循环的`else`子句?

ale*_*xis 187 python loops for-loop while-loop

许多Python程序员可能没有意识到while循环和for循环的语法包含一个可选else:子句:

for val in iterable:
    do_something(val)
else:
    clean_up()
Run Code Online (Sandbox Code Playgroud)

else子句的主体是某些清理操作的好地方,并且在循环的正常终止时执行:即,退出循环returnbreak跳过该else子句; continue执行后退出.我知道这只是因为我只是看着它(再次),因为我永远记得else子句被执行.

总是?关于循环的"失败",顾名思义?定期终止?即使循环退出return?如果不抬头,我永远无法完全确定.

我责怪我对关键词选择的持续存在的不确定性:我发现else这种语义非常缺乏统一性.我的问题不是"为什么这个关键字用于此目的"(我可能会投票关闭,但只有在阅读了答案和评论后),但我怎么能想到else关键字,以便它的语义有意义,我因此能记得吗?

我确信对此有相当多的讨论,我可以想象选择是为了与try语句的else:条款(我也必须查找)保持一致,并且目标是不添加到列表中Python的保留字.也许选择的理由else将澄清其功能并使其更令人难忘,但我将名称与功能联系起来,而不是在历史解释本身之后.

这个问题的答案,我的问题简要地作为副本被关闭,包含了很多有趣的背景故事.我的问题有一个不同的焦点(如何连接else关键字选择的特定语义),但我觉得应该有一个链接到这个问题的某个地方.

dra*_*woc 210

(这是受@Mark Tolonen的回答启发的.)

如果if语句else的条件求值为false,则语句将运行其子句.同样,如果while循环的条件求值为false ,则循环运行else子句.

此规则与您描述的行为相匹配:

  • 在正常执行中,while循环重复运行,直到条件计算为false,因此自然退出循环运行else子句.
  • 执行break语句时,退出循环而不评估条件,因此条件不能计算为false,并且您永远不会运行else子句.
  • 执行continue语句时,再次评估条件,并完成您在循环迭代开始时的正常操作.因此,如果条件为true,则保持循环,但如果为false,则运行else子句.
  • 退出循环的其他方法,例如return,不评估条件,因此不运行else子句.

for循环的行为方式相同.如果迭代器具有更多元素,则将条件视为true,否则将其视为false.

  • 这是一个非常好的答案.像一系列elif语句一样对待你的循环,else行为将暴露其自然逻辑. (8认同)
  • 确切地说,一个while循环可能在条件"break"之前满足False,在这种情况下,"else"不会运行但条件为False.与`for`循环类似,它可以在最后一个元素上"break". (2认同)

Mos*_*oye 36

最好以这种方式来考虑它:如果前一个块中的所有内容都正确,那么else始终执行该块,以使其达到耗尽状态.for

用鼠标右键在这种情况下将意味着没有exception,没有break,没有return.任何劫持控制权的声明for都会导致该else块被绕过.


搜索某个项目时会发现一个常见的用例iterable,当找到该项目或"not found"通过以下else块提出/打印标记时,将搜索该项目:

for items in basket:
    if isinstance(item, Egg):
        break
else:
    print("No eggs in basket")  
Run Code Online (Sandbox Code Playgroud)

A continue不会劫持控制for,因此控制将elsefor耗尽后继续进行.

  • 听起来很不错......但是当你的事情*不正确时你会期望执行'else`条款,不是吗?我已经混淆了...... (20认同)

Mar*_*nen 31

什么时候if执行else?当它的条件是假的.它与while/ 完全相同else.所以你可以认为while/ else只是一个if在它评估为false之前一直运行它的真实条件.A break不会改变这一点.它只是包含循环的跳转而没有评估.该else如果仅执行评估if/ while条件为假.

for是类似的,除了它的错误条件是耗尽它的迭代器.

continue并且break不执行else.那不是他们的功能.在break退出循环含有.将continue返回到循环包含,其中,所述循环条件被评估的顶部.它是评估if/ while错误(或for没有更多项目)执行的行为,else而不是其他方式.

  • @alexis是的我需要在那里澄清一下.编辑.continue不会执行else,但会返回到循环的顶部,然后可能会计算为false. (4认同)

Ale*_*all 24

这就是它的本质意义:

for/while ...:
    if ...:
        break
if there was a break:
    pass
else:
    ...
Run Code Online (Sandbox Code Playgroud)

这是编写这种常见模式的更好方法:

found = False
for/while ...:
    if ...:
        found = True
        break
if not found:
    ...
Run Code Online (Sandbox Code Playgroud)

else如果有一个return因为return离开函数,则不会执行该子句.您可能想到的唯一例外是finally,其目的是确保它始终执行.

continue与此事无关.它导致循环的当前迭代结束,这可能发生在整个循环结束,并且显然在这种情况下循环不是由a结束break.

try/else 类似:

try:
    ...
except:
    ...
if there was an exception:
    pass
else:
    ...
Run Code Online (Sandbox Code Playgroud)


Kei*_*wan 20

如果你认为你的循环是一个类似于此的结构(有点伪代码):

loop:
if condition then

   ... //execute body
   goto loop
else
   ...
Run Code Online (Sandbox Code Playgroud)

它可能会更有意义.循环本质上只是一个if重复的语句,直到条件为止false.这是重点.循环检查它的状态并看到它false,因此执行else(就像正常一样if/else)然后完成循环.

因此,请注意在else 检查条件时执行唯一get.这意味着如果您在执行过程中使用例如a return或a 退出循环体break,因为不再检查条件,else则不会执行该情况.

continue另一方面,A 停止当前执行,然后再跳回以再次检查循环的状态,这就是else在这种情况下可以达到的原因.


nas*_*-sh 14

我对循环else条款的关注时刻是我正在观看Raymond Hettinger的演讲时,他讲述了一个关于他应该如何调用它的故事nobreak.看看下面的代码,您认为它会做什么?

for i in range(10):
    if test(i):
        break
    # ... work with i
nobreak:
    print('Loop completed')
Run Code Online (Sandbox Code Playgroud)

你会猜到它会怎么样?好吧,nobreak只有break在循环中没有命中语句时才会执行所说的部分.


Tad*_*sen 7

通常我倾向于想到这样的循环结构:

for item in my_sequence:
    if logic(item):
        do_something(item)
        break
Run Code Online (Sandbox Code Playgroud)

要像可变数量的if/elif语句一样:

if logic(my_seq[0]):
    do_something(my_seq[0])
elif logic(my_seq[1]):
    do_something(my_seq[1])
elif logic(my_seq[2]):
    do_something(my_seq[2])
....
elif logic(my_seq[-1]):
    do_something(my_seq[-1])
Run Code Online (Sandbox Code Playgroud)

在这种情况下,elsefor循环上的else语句与elifs 链上的语句完全相同,只有在它之前没有任何条件计算为True时才会执行.(或打破执行return或异常)如果我的循环不符合此规范,我通常会选择退出使用for: else,因为您发布此问题的确切原因:它不直观.


Fab*_*olm 6

其他人已经解释过它的机制while/for...else,而且Python 3语言参考有权威的定义(参见whilefor),但这是我的个人助记符FWIW.我想我的关键是将其分解为两部分:一部分用于理解else与循环条件相关的含义,一部分用于理解循环控制.

我发现最简单的理解是while...else:

while你有更多的东西,做的东西,else如果你用完了,这样做

for...else助记符是基本相同的:

for每个项目,做的东西,但else如果你用完了,这样做

在这两种情况下,else只有在没有更多要处理的项目时才会到达该部分,并且最后一项已经以常规方式处理(即无breakreturn).一个continue刚刚回到如果有任何更多的项目看.我对这些规则的助记符适用于:whilefor:

当进入break或进入时,return没有什么else可做的,
当我说continue,那就是为你"循环回来"

- 用"循环回启动"意味着,显然,循环的开始,我们检查迭代中是否还有更多的项目,所以就此else而言,continue根本不起作用.

  • 我建议可以通过说for/else循环的通常目的是检查项目*直到找到你想要的东西并想要停止*,或者你用完项目来增强它."其他"存在处理"你用完物品(没有找到你要找的东西)"部分. (4认同)

Bar*_*zKP 6

测试驱动开发(TDD)中,当使用Transformation Priority Premise范例时,您将循环视为条件语句的泛化.

如果您只考虑简单if/else(无elif)语句,这种方法与此语法结合得很好:

if cond:
    # 1
else:
    # 2
Run Code Online (Sandbox Code Playgroud)

概括为:

while cond:  # <-- generalization
    # 1
else:
    # 2
Run Code Online (Sandbox Code Playgroud)

很好.

在其他语言中,从单个案例到具有集合的案例的TDD步骤需要更多重构.


以下是8thlight博客的示例:

在8thlight博客的链接文章中,我们考虑使用Word Wrap kata:在字符串中添加换行符(s下面的代码段中的变量),使它们适合给定的宽度(length下面片段中的变量).有一次,实现如下(Java):

String result = "";
if (s.length() > length) {
    result = s.substring(0, length) + "\n" + s.substring(length);
} else {
    result = s;
}
return result;
Run Code Online (Sandbox Code Playgroud)

而目前失败的下一个测试是:

@Test
public void WordLongerThanTwiceLengthShouldBreakTwice() throws Exception {
    assertThat(wrap("verylongword", 4), is("very\nlong\nword"));
    }
Run Code Online (Sandbox Code Playgroud)

因此,我们的代码可以有条件地运行:当满足特定条件时,会添加换行符.我们希望改进代码以处理多个换行符.文章中提出的解决方案建议应用(if-> while)转换,但作者发表评论:

虽然循环不能有else子句,所以我们需要通过在else路径中少做一些来消除if路径.同样,这是一个重构.

它强制在一个失败测试的上下文中对代码进行更多更改:

String result = "";
while (s.length() > length) {
    result += s.substring(0, length) + "\n";
    s = s.substring(length);
}
result += s;
Run Code Online (Sandbox Code Playgroud)

在TDD中,我们希望尽可能少地编写代码以使测试通过.感谢Python的语法,可以进行以下转换:

从:

result = ""
if len(s) > length:
    result = s[0:length] + "\n"
    s = s[length:]
else:
    result += s
Run Code Online (Sandbox Code Playgroud)

至:

result = ""
while len(s) > length:
    result += s[0:length] + "\n"
    s = s[length:]
else:
    result += s
Run Code Online (Sandbox Code Playgroud)


Win*_*ert 5

我看到它的方式,else:当你迭代循环结束时触发.

如果您breakreturnraise你不重复过去的循环的结尾,你停下immeadiately,因而else:块将不会运行.如果你continue仍然迭代循环结束,那么继续只是跳到下一次迭代.它不会停止循环.


Mag*_*ero 0

带有while子句else的语句

\n
while condition:\n    iteration\nelse:\n    conclusion\n
Run Code Online (Sandbox Code Playgroud)\n

完全等于

\n
while True:\n    if not condition:\n        conclusion\n        break\n    iteration\n
Run Code Online (Sandbox Code Playgroud)\n

带有for子句else的语句

\n
for item in iterable:\n    iteration\nelse:\n    conclusion\n
Run Code Online (Sandbox Code Playgroud)\n

完全等于

\n
iterator = iter(iterable)\nwhile True:\n    try:\n        item = next(iterator)\n    except StopIteration:\n        conclusion\n        break\n    iteration\n
Run Code Online (Sandbox Code Playgroud)\n

它有助于理解迭代语句中breakor语句的效果。continue

\n

笔记。\xe2\x80\x94 对于不带子句的whileand语句,将结论语句替换为等效代码中的语句。forelsepass

\n