为什么python在for和而循环之后使用“其他”?

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

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程序员是如何在他们的脑海中阅读这个结构的(或者大声地,如果你喜欢的话)。也许我错过了一些让这样的代码块更容易破译的东西?


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

279681 次浏览

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

我读到它类似于:

如果还在运行循环的条件下,做点什么,否则做点别的。

你可以这样想, else和其他东西一样,或者其他东西,这不是在循环中完成的。

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

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或whel-else块。

请参阅[Python想法]for… else线程摘要

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

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

使用Pythonforelse构造

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

我认为留档对否则继续有很好的解释

[…]当循环通过耗尽列表(使用for)或条件变为false(使用这时)而终止时,它会被执行,但当循环由Brex语句终止时则不会执行。

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

Raymond Hettinger有一个很棒的演讲,题目是将代码转换为漂亮的惯用Python,他在演讲中简要介绍了for ... else构造的历史。相关部分是“区分循环中的多个出口点”从15:50开始,持续了大约三分钟。以下是要点:

  • for ... else结构由Donald Knuth设计,作为某些GOTO用例的替代品;
  • 重复使用else关键字是有意义的,因为“这是Knuth使用的,人们知道,当时所有[for语句]都在下面嵌入了ifGOTO,他们期望else;”
  • 事后看来,它应该被称为“无休息”(或者可能是“无休息”),然后它就不会令人困惑了。

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

就个人而言,我喜欢在else可能被误认为属于循环内部的任何地方在线评论# no break的妥协。它相当清晰简洁。这个选项在比约恩链接的摘要中得到了简短的提及在他的答案的末尾:

为了完整起见,我应该提到,在 语法,想要这种语法的程序员现在就可以拥有它:

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

*视频中的额外引用:“就像我们调用lambdamakeFunction一样,没有人会问,'lambda做什么?'”

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

我发现最简单的方法来“了解”for/else做了什么,更重要的是,何时使用它,是专注于off语句跳转到哪里。For/else结构是一个块。换行符跳出块,因此跳过“over”的其他子句。如果alia子句的内容只是跟着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')

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

第二个更好的想法…

没有中断注释是否定的。理解一个肯定的断言要容易得多,那就是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

我同意,这更像是一个“elif not[条件(s)提高休息]”。

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

对我来说,有三种方法可以在For... elseWhile... else语句中“读取”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没有“中断”是毫无意义的,所以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
----------

由于技术部分已经回答了很多,我的评论只是与产生这个回收关键字的混乱有关。

由于Python是一种非常雄辩的编程语言,因此滥用关键字更为臭名昭著。else关键字完美地描述了决策树流程的一部分,“如果你不能这样做,(否则)去做”。这是我们自己的语言中的暗示

相反,将此关键字与whilefor语句一起使用会造成混淆。原因是,我们的程序员职业生涯告诉我们else语句驻留在决策树中;它的逻辑范围有条件返回一条要遵循的路径的包装器。同时,循环语句有一个形象的明确目标来实现某些目标。目标是在流程的持续迭代后实现的。

if / else指示要遵循的路径。循环沿着一条路走,直到“目标”完成

问题是else是一个明确定义条件中最后一个选项的词。该词的语义学既是Python的共享,也是Human Language的共享。但是Human Language中的else词从不用于表示某人或某物在完成某事后会采取的操作。如果在完成它的过程中出现问题(更像是打破语句),它会被使用。

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

这类似于一些孩子在组装玩具的每一步之后的情况:然后什么爸爸?

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:关掉并继续语句,以及循环上的其他子句

循环语句可能有一个else子句;当循环通过耗尽列表(使用for)或条件变为false(使用这时)而终止时,它会被执行,但当循环被Brex语句终止时不会执行。这由以下搜索素数的循环举例说明:

>>> 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语句。)

当与循环一起使用时,与if语句相比,try子句与try语句的ther子句有更多的共同点:try语句的else子句在没有异常发生时运行,循环的else子句在没有中断发生时运行。有关try语句和异常的更多信息,请参阅处理异常。

继续语句,也从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

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

是的,我知道这种比较可能是困难和令人厌倦的,但它确实澄清了混淆。

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

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

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

除了搜索之外,这是另一个惯用的用例。假设你想等待一个条件为真,例如远程服务器上的端口打开,以及一些超时。然后你可以像这样使用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()
for i in range(3):
print(i)


if i == 2:
print("Too big - I'm giving up!")
break;
else:
print("Completed successfully")

这里的“其他”非常简单,只是意味着

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

我只是想让自己再次理解它。我发现以下内容有帮助!

•将else与循环内的if配对(而不是与for配对)-如果满足条件,则打破循环,否则这样做-除了它是一个else与多个if配对!
如果没有#EYZ完全满意,那么做else。 •多个if实际上也可以被认为是if-elifs!

这里有一种思考它的方法,我没有看到其他人在上面提到过:

首先,请记住,for-loops基本上只是arm-loops周围的语法糖。例如,循环

for item in sequence:
do_something(item)

可以重写(近似)为

item = None
while sequence.hasnext():
item = sequence.next()
do_something(item)

其次,请记住,虽然循环基本上只是重复的if-块!你总是可以读一个同时循环为“如果这个条件为真,执行主体,然后回来再次检查”。

因此,虽然/ther非常有意义:它的结构与if/ther完全相同,但增加了循环直到条件变为false的功能,而不是只检查一次条件。

然后for/ther也很有意义:因为所有的for循环都只是虽然循环之上的语法糖,你只需要弄清楚底层的同时循环的隐含条件是什么,然后当条件变为False时,其他对应。

我考虑的结构是(if)A,其他B,和(if)-其他是一个特别的如果大致。它可能有助于理解否则

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

for(if)可以被认为是一个特殊的if,它会做一个循环来尝试满足if条件。一旦满足如果条件,A和打破其他, B。

伟大的答案是:

  • 这个解释历史,和
  • 这个给出了正确的 引文以方便您的翻译/理解。

我在这里的注释来自Donald Knuth曾经说过(抱歉找不到参考),有一个构造,其中whel-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可以被认为是whle-else的语法糖,它在if块的末尾隐式地包含break。相反的含义,while循环是if的扩展,更常见(它只是重复/循环条件检查),因为if经常在while之前被教授。然而这不是真的,因为这意味着当条件为假时,whle-else中的else块将被执行每次

为了减轻你的理解,这样想:

没有breakreturn等,循环仅在条件不再为真时结束,在这种情况下else块也将执行一次。在Pythonfor的情况下,您必须考虑C风格的for循环(带条件)或将它们转换为while

另一个注意事项:

过早的breakreturn等内部循环使得条件不可能变成假,因为执行跳出了循环,而条件为真,它永远不会再回来检查它。

for i in range(10):
print(i)


if i == 9:
print("Too big - I'm giving up!")
break;
else:
print("Completed successfully")

如果i=9,则循环将结束。而任何如果条件都不太满意,那么else将完成其余部分。

我想知道Python程序员是如何在他们的脑海中读取这个结构的(或者大声地,如果你喜欢的话)。

我只是在脑子里想:

"否则没有遇到break…"

就是它了!

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

参考:

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

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


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

在循环正常完成后执行else子句。这意味着:==> 只有当循环没有被一个中断语句终止时,在for/同时之后的其他块才会被执行

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

无论循环是否进入其主体,循环的else分支都会执行一次,除非进入了循环主体但没有结束。也就是说,在循环内部遇到了一个关掉或返回语句。

my_list = []
for i in my_list:
print(i, end=',')
else:
print('loop did not enter')


##################################


for i in range(1,6,1):
print(i, end=',')
else:
print('loop completed successfully:', i)


##################################


for i in range(1,6,1):
if i == 3:
print('loop did not finish:', i)
break
print(i, end=',')
else:
print('else:', i)

输出:

loop did not enter
1,2,3,4,5,loop completed successfully: 5
1,2,loop did not finish: 3

其他时间也是一样的。

import random
random.seed(8)


i = 100
while i < 90:
print(i, end=',')
i = random.randint(0,100)
else:
print('loop did not enter:', i)


##################################


i = 25
while i < 90:
print(i, end=',')
i = random.randint(0,100)
else:
print('loop completed successfully:', i)


##################################


i = 25
while i < 90:
if i % 10 == 0:
print('loop did not finish:', i)
break
print(i, end=',')
i = random.randint(0,100)
else:
print('else:', i)

输出:

loop did not enter: 100
25,29,47,48,16,24,loop completed successfully: 90
25,5,loop did not finish: 10