许多python程序员可能不知道while循环和for循环的语法包括可选的else:子句:
1 2 3 4
| for val in iterable:
do_something(val)
else:
clean_up() |
else条款的主体是某种清理行动的良好场所,在循环正常终止时执行:即使用return或break跳过else条款退出循环;在continue执行后退出。我之所以知道这一点,仅仅是因为我刚刚(再一次)查阅了它,因为我永远记不清何时执行了else子句。
总是?关于循环的"失败",顾名思义?定期终止?即使循环使用return退出?我不能不抬头就完全确定。
我把一直以来的不确定性归咎于关键字的选择:我发现else在这个语义上非常不通顺。我的问题不是"为什么这个关键字用于这个目的"(我可能会投票结束,虽然只有在阅读了答案和评论之后),而是我如何思考else关键字,以便它的语义有意义,因此我可以记住它?
我相信关于这个问题已经有了相当多的讨论,我可以想象这样的选择是为了与try语句的else:子句保持一致(我也必须查找它),目的是不将它添加到python的保留字列表中。也许选择else的原因将澄清它的功能,使它更令人难忘,但我是在将名称与功能联系起来之后,而不是在历史解释之后。
这个问题的答案,我的问题作为的一个副本,简短地封闭了,包含了许多有趣的背景故事。我的问题有一个不同的焦点(如何将else的特定语义与关键字choice连接起来),但我觉得应该在某个地方有一个指向这个问题的链接。
- 如果还有什么东西要重复…"其他"
- 哇,太快了。链接问题的焦点几乎是我不想问的问题(正如我在问题中解释的那样),但我会选择"如果找到…否则:"作为足够的解释。
- @蟋蟀,这可能接近标记,但如果循环通过与空字符串(和许多循环一样)比较来检查EOF,那将是完全错误的。
- 我想在写下这个问题之后,你现在可以记住它了。)
- @贾斯珀,我也这么想!:-)我想几周后就会知道……
- 如果你能在10秒内用谷歌搜索,为什么你想记住一个难以记住的稀有功能?
- else基本上是指"如果延续条件失败"。在传统的for循环中,延续条件通常是i < 42,在这种情况下,您可以将该部分视为if i < 42; execute the loop body; else; do that other thing。
- 这都是真的,我特别喜欢drawoc的答案,但另一个需要考虑的是,其他关键字是可用的,在语法上也有一些好的意义。您可能知道try/except,也可能知道try/except/finally,但它也有其他功能——如果没有发生异常,请运行此代码。这是顺便说一句,与在try子句下推送此代码不同——当目标狭隘时,最好使用异常处理。所以,虽然它在概念上是有意义的——根据这里的许多答案——我认为这也是关键词在游戏中的重用——在特定条件下运行它。
- 我的头脑被这一点吹了,因为我经常想用Java构建其他构造。现在我发现它已经存在了,我一直想要它的确切语义,但是在Python中呢?这只是一个很好但最终不必要的伎俩,一个设计委员会的语言,如Java将永远不会添加。哦,好吧。
- 我似乎记得Perl也有类似的东西。用于从主体(continue、break)中修改for循环的关键字也用于后缀语句。我记不清具体细节,只记得它不同于C语言中的关键词。
- JD?这可能是:正如我后来发现的那样,"for/else构造是由DonaldKnuth设计的,作为某些goto语句的替换。"Knuth使用了else,这在当时很有意义,因为循环是在循环控制语句之前完成的。
- JD?但也许你在考虑Perl的后缀if,它可以处理任何事情,比如break if $var;、print"Found it!" if $found;等。
- 不,这不是我想的。请看这里。
- 嗯,我想我不知道这个特别的功能(它似乎从2011年开始)。它与break不同,因为它是重复执行的。还有对经典循环的另一种扩展,可能和其他循环一样注定要被遗忘…(guido提到,如果让他重新做一遍,他将不会以任何名称在python中提供循环其他功能。)
- 请原谅我(可能很愚蠢)的问题,但是这个功能到底有什么用呢?for val in iterable: { do_something(val) } else: { clean_up() }和for val in iterable: { do_something(val) } clean_up()不完全一样吗(我不得不用大括号代替换行符,因为我不知道如何在注释中添加它们)
- @falanwe,当代码被break退出时有区别。规范的用例是当循环搜索某个东西时,当找到它时就会中断。只有在未发现任何情况下,才会执行else。
- 高度相关:python notes.curousefficiency.org/en/latest/python-concepts&zwnj;&8203;/&hellip;
(这灵感来自@mark tolonen的回答。)
如果条件的计算结果为假,则if语句将运行其else子句。同样,如果条件的计算结果为false,则while循环运行else子句。
此规则与您描述的行为匹配:
- 在正常执行中,while循环反复运行,直到条件的计算结果为false,因此自然退出循环会运行else子句。
- 当您执行break语句时,在不评估条件的情况下退出循环,因此条件的值不能为false,并且您从不运行else子句。
- 当您执行一个continue语句时,您将再次评估条件,并按照您在循环迭代开始时通常会做的那样做。因此,如果条件为真,则继续循环,如果条件为假,则运行else子句。
- 其他退出循环的方法,如return,不评估条件,因此不运行else子句。
for循环的行为方式相同。如果迭代器有更多的元素,只需将条件视为真,否则视为假。
- 这是一个非常好的答案。将循环视为一系列elif语句,else行为将公开其自然逻辑。
- 我也喜欢这个答案,但它并不是用一系列elif声明来做类比。有一个答案是肯定的,而且它有一个净上票。
- 不完全正确,一个while循环可以让条件在它之前满足false right,在这种情况下,else不会运行,但条件是false。与for循环类似,它可以在最后一个元素上break循环。
它认为这样更好:else块执行,如果一切都行,我是它在搜索那块preceding for耗尽它。
这意味着正确的上下文exceptionbreakNO,NO,NO return。任何想要控制语句是从hijacks forelsebypassed块造成的。
一个普通的用例是当寻找到了在这项研究中iterable,搜索是为OFF。当一个项目或一"not found"标志是提出了通过以下else/印刷块:
1 2 3 4 5
| for items in basket:
if isinstance(item, Egg):
break
else:
print("No eggs in basket") |
a continue不hijack控制从控制for,想继续到else后for是筋疲力尽。
- 听起来很不错…但如果事情不顺利的话,你会期望执行一个else条款,对吗?我又开始困惑了…
- 我不得不不同意你的观点,"从技术上讲,它与其他的else在语义上并不相似",因为else运行时,for循环中的任何条件都不评估为真,正如我在回答中所证明的那样。
- @TadhgMcDonald Jensen您也可以在False上断开回路。因此,如何破坏for的问题取决于用例。
- 没错,我想找一种方法来解释"else"在英语中的含义(这确实反映在Python中else的其他用法中)。你提供了一个很好的直观的总结,关于以东十一〔0〕所做的,@moses,而不是关于我们如何将这种行为与"其他"联系起来。如果使用了不同的关键字(例如,nobreak,如本回答中对相关问题所述),则更容易理解。
- @亚历克西斯从你添加的链接,鉴于这个else的词源,我认为我们必须接受现状:)
- 你说现状是什么意思?我当然不是想改变Python的语法!
- 这个else的行为与它听起来的不一样。为了理解它,您必须应用一些抽象的东西,比如nobreak,每个人都已经提出过。
- 这与"事情进展顺利"毫无关系。当if/while条件的评估结果为假或for不在项中时,则执行其他操作。break存在包含循环(在else之后)。continue返回并再次评估循环条件。
- @大多数情况下,在if/elif的链中,从链中到break的唯一方法是将条件之一设为True,其中作为for循环,退出时显式break(两者都被return或exception破坏),当for循环需要中断时,我当然不会认为事情会"出错"。
- 向右走是过于简单化了,我已经在答案中描述了我的意思。
当执行是在if在else?当它的状态是FALSE。它是完全相同的while/ else。所以你想whileAS / elseif只是保持运行状态,直到它evaluates其真假。这是一break不变化。它只是一个跳跃(含环和没有评价。如果这是唯一的else评价执行《if/ while状态是FALSE。
在for是相似的,除了它的状态是其迭代器返回排气。
continue和breakelse不要执行。这不是他们的功能。含氟环break出口的位置。回到酒店去的continue含顶环,在环的状态是评价。它是评价if/ while法案到FALSE(或没有forITEMS)是executes else和没有其他的方式。
- 你说的话听起来很明智,但把这三个终止条件加在一起,"直到[条件]是错误的或中断/继续"是错误的:至关重要的是,如果循环是用continue退出的(或正常情况下),则执行else子句,而不是用break退出的。这些微妙之处正是我试图真正摸索江户十一号(0)捕获的和它不捕获的原因。
- @亚历克西斯是的,我需要澄清一下。编辑。Continue不执行else,但返回到循环的顶部,然后该顶部的值可能为false。
这是什么不均值:
1 2 3 4 5 6 7
| for/while ...:
if ...:
break
if there was a break:
pass
else:
... |
这是一个更好的方式在普通模式:写作
1 2 3 4 5 6 7
| found = False
for/while ...:
if ...:
found = True
break
if not found:
... |
在else条款将不执行,如果有一个returnreturn叶子,因为它是一个函数,是用来。唯一的例外是,你可能是他的finally思路,目的是确保它总是执行。
有没有什么特别continue待办事项这一物。它的原因。当前迭代的循环结束它可能发生到结束的整个回路,回路中的案例和部分是不是由一break端。
try/else是相似的:
1 2 3 4 5 6 7 8
| try:
...
except:
...
if there was an exception:
pass
else:
... |
如果你认为你的循环作为一个结构类似于本(在伪代码):
1 2 3 4 5 6 7
| loop:
if condition then
... //execute body
goto loop
else
... |
它可以做一点更多的镰刀。一环是不只是一if声明这是重复直到false状态。这是一个重要的点。回路检查其状态和它的false湖,因此executes else(就像一个正常的if/else),然后是无环路。
因此,这else只得到通知的执行,当状态是检查。这意味着,如果你退出Body of the loop中执行,例如returnbreakA或A,因为状态不好的情况,else不会被执行。
在另一continue停止当前执行的手,然后跳回到检查状态的一个回路,这就是为什么else可以达到在这个场景。
- 我很喜欢这个答案,但你可以简化:去掉end标签,把goto loop放在if体内。也许把if放在标签的同一行,甚至会显得更为过时,它突然看起来非常像原始符号。
- @是的,我想这会让事情更清楚一点,谢谢。
我对循环的else条款的理解是在我观看雷蒙德·赫廷格的演讲时,他讲了一个他认为应该如何称之为nobreak的故事。看看下面的代码,您认为它会做什么?
1 2 3 4 5 6
| for i in range(10):
if test(i):
break
# ... work with i
nobreak:
print('Loop completed') |
你猜是什么?好吧,说nobreak的那部分只有在循环中没有命中break语句时才会被执行。
通常,我想这样:A环结构
1 2 3 4
| for item in my_sequence:
if logic(item):
do_something(item)
break |
一个焊料if/elif样变数的语句:
1 2 3 4 5 6 7 8 9
| 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]) |
在这个案例的elsefor语句在循环语句的作品像是在elseelifs链,它只executes如果不在它的条件,是真的。(或执行一个中断或异常return)如果我不适合本规范环通常使用的第一选择退出的确切原因for: else你张贴这个问题:它是非直观的。
- 正确的。但是一个循环会运行多次,所以不清楚如何将其应用于for循环。你能澄清一下吗?
- @亚历克西斯,我已经重做了我的答案,我想现在更清楚了。
在测试驱动开发(TDD)中,当使用转换优先级前提范式时,您将循环视为条件语句的泛化。
如果只考虑简单的if/else语句(没有elif语句),则此方法与此语法结合得很好:
1 2 3 4
| if cond:
# 1
else:
# 2 |
推广到:
1 2 3 4
| while cond: # <-- generalization
# 1
else:
# 2 |
很好地。
在其他语言中,从单个案例到集合案例的TDD步骤需要更多的重构。
下面是8thlight博客的一个例子:
在8thlight博客的链接文章中,我们考虑了换行片:在字符串中添加换行符(下面片段中的s变量),使其适合给定的宽度(下面片段中的length变量)。在某种程度上,实现看起来如下(Java):
1 2 3 4 5 6 7 8
| String result ="";
if (s.length() > length) {
result = s.substring(0, length) +"
" + s.substring(length);
} else {
result = s;
}
return result; |
下一个测试,目前失败的是:
1 2 3 4 5 6
| @Test
public void WordLongerThanTwiceLengthShouldBreakTwice() throws Exception {
assertThat(wrap("verylongword", 4), is("very
long
word"));
} |
所以我们有条件地工作的代码:当满足特定条件时,会添加一个换行符。我们希望改进代码以处理多个换行符。本文提出的解决方案建议应用(if->while)转换,但作者提出的意见是:
While loops can’t have else clauses, so we need to eliminate the else path by doing less in the if path. Again, this is a refactoring.
在一个失败的测试环境中,强制对代码进行更多的更改:
1 2 3 4 5 6 7
| String result ="";
while (s.length() > length) {
result += s.substring(0, length) +"
";
s = s.substring(length);
}
result += s; |
在TDD中,我们希望尽可能少地编写代码以使测试通过。由于python的语法,以下转换是可能的:
来自:
1 2 3 4 5 6 7
| result =""
if len(s) > length:
result = s[0:length] +"
"
s = s[length:]
else:
result += s |
到:
1 2 3 4 5 6 7
| result =""
while len(s) > length:
result += s[0:length] +"
"
s = s[length:]
else:
result += s |
- 我不知道这是否有水,因为我不熟悉TPP模式,即使在点击维基百科页面后,也有太多的材料需要吸收。你说清楚一个循环如何成为条件语句的泛化,这到底意味着什么?
- @亚历克西斯,我增加了一个例子。我正在尝试制定另一个方案,以展示单个案例的例子->案例集合。
- @我很好,谢谢!固定:-)
其他人已经解释了while/for...else的机制,python 3语言引用有权威的定义(见while和for),但这里是我的个人助记符fwiw。我想对我来说,关键是把它分成两部分:一部分是理解与循环条件相关的else的含义,另一部分是理解循环控制。
我发现最简单的方法是从了解while...else开始:
while you have more items, do stuff, else if you run out, do this
for...else助记法基本相同:
for every item, do stuff, but else if you run out, do this
在这两种情况下,只有当没有更多的项目需要处理,并且最后一个项目已按常规处理(即没有break或return)时,才会到达else部分。一个continue只是回去看看还有没有其他的东西。本规则的助记法适用于while和for:
when breaking or returning, there's nothing else to do,
and when I say continue, that's"loop back to start" for you
&ndash;使用"循环返回开始"显然意味着循环的开始,在这里我们检查iterable中是否还有其他项目,就else而言,continue实际上根本不起作用。
- 我建议通过说for/else循环的通常目的是检查项目,直到找到要查找的内容并想停止,或者项目用完。"else"用于处理"物品用完(未找到所需物品)"部分。
- @超级卫星:可能是,但我不知道最常见的用途是什么。else还可以用来做一些简单的事情,当你完成了所有的项目。例如,写一个日志条目,更新一个用户界面,或者向其他进程发出你已经完成的信号。任何事,真的。另外,有些代码以循环中的break结尾"成功",而else用于处理在迭代过程中找不到合适项的"错误"情况(也许这就是您所想的?).
- 我所想到的情况正是这样的,成功的案例以"中断"结束,而"其他"则处理了失败。如果循环中没有"break",那么"else"代码也可以作为封闭块的一部分简单地跟随循环。
- 除非您需要区分循环在没有中断的情况下(这是一个成功的情况)通过所有不可重复项的情况,否则您必须将"最终确定"代码放入循环的else块中,或者使用其他方法跟踪结果。我基本同意,我只是说我不知道人们是如何使用这个功能的,因此我想避免假设"else处理成功案例"场景还是"else处理失败案例"场景更常见。但你有一个好观点,所以评论投反对票!
在我看来,当您遍历循环的末尾时,else:将触发。
如果你不迭代通过循环的末尾,你会立即停止,这样,else:块就不会运行。如果您continue仍在循环结束时迭代,因为continue只是跳到下一个迭代。它不会停止循环。
- 我喜欢这样,我觉得你在做什么。它与在循环关键字之前糟糕的过去几天如何实现循环有点联系。(也就是说,检查放在循环的底部,goto是成功的顶部。)但它是最热门答案的较短版本…
- @亚历克西斯,主观的,但我发现我的表达方式更容易思考。
- 实际上我同意。如果只是因为它更简洁。
把else子句看作是循环构造的一部分;break完全脱离了循环构造,因此跳过了else子句。
但实际上,我的心理映射仅仅是模式C/C++模式的"结构化"版本:
1 2 3 4 5 6 7 8
| for (...) {
...
if (test) { goto done; }
...
}
...
done:
... |
因此,当我遇到for...else或自己编写它时,而不是直接理解它,我会在心里将它转换为对模式的上述理解,然后计算出python语法的哪些部分映射到模式的哪些部分。
(我把"结构化"放在恐吓引号中,因为区别不在于代码是结构化的还是非结构化的,而在于是否有专门用于特定结构的关键字和语法)
- else在哪里?如果你是说done:标签代表代理或else:,我相信你是完全相反的。
- @亚历克西斯的"else"代码会在done:标签前直接填写"…"。总的来说,也许最好这样说:python具有elseon循环结构,这样您就可以在不使用goto的情况下表示这个控制流模式。
- 还有其他方法可以执行此控制流模式,例如通过设置标志。这就是else所避免的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| # tested in Python 3.6.4
def buy_fruit(fruits):
'''I translate the 'else' below into 'if no break' from for loop '''
for fruit in fruits:
if 'rotten' in fruit:
print(f'do not want to buy {fruit}')
break
else: #if no break
print(f'ready to buy {fruits}')
if __name__ == '__main__':
a_bag_of_apples = ['golden delicious', 'honeycrisp', 'rotten mcintosh']
b_bag_of_apples = ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
buy_fruit(a_bag_of_apples)
buy_fruit(b_bag_of_apples)
'''
do not want to buy rotten mcintosh
ready to buy ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
''' |
如果你想把else和for结合在一起,可能会让人困惑。我不认为关键字else是这种语法的一个很好的选择,但是如果您将else与break配对,您会发现它实际上是有意义的。
让我用人类语言演示一下。
for each person in a group of suspects if anyone is the criminal
break the investigation. else report failure.
如果for循环中没有break,那么else几乎没有用处。
在我看来,关键是要考虑continue的含义,而不是else的含义。
您提到的其他关键字break out of the loop(exit abnormally),而continue没有,它只是跳过了循环中代码块的其余部分。它可以先于循环终止这一事实是偶然的:终止实际上是通过循环条件表达式的计算以正常方式完成的。
然后您只需要记住,在正常循环终止后执行else子句。