Why does python use 'else' after for and while loops?
我了解这种结构的工作原理:
1 2 3 4 5 6 7 8 | for i in range(10): print(i) if i == 9: print("Too big - I'm giving up!") break; else: print("Completed successfully") |
但我不明白为什么这里使用
我想知道Python编码人员是如何在他们的头脑中阅读这个结构的(或者大声朗读,如果您愿意的话)。也许我遗漏了一些可以使这样的代码块更容易识别的东西?
一个常见的构造是运行一个循环,直到找到某个东西,然后从循环中中断。问题是,如果我脱离了循环或者循环结束,我需要确定发生了什么情况。一种方法是创建一个标志或存储变量,让我进行第二次测试,看看循环是如何退出的。
例如,假设我需要搜索一个列表并处理每个项目,直到找到一个标志项目,然后停止处理。如果缺少标志项,则需要引发异常。
使用python
1 2 3 4 5 6 | for i in mylist: if i == theflag: break process(i) else: raise ValueError("List argument missing terminal flag.") |
将此方法与不使用此语法结构的方法进行比较:
1 2 3 4 5 6 7 8 9 | flagfound = False for i in mylist: if i == theflag: flagfound = True break process(i) if not flagfound: raise ValueError("List argument missing terminal flag.") |
在第一种情况下,
即使对经验丰富的Python编码人员来说,这也是一个奇怪的构造。当与for循环结合使用时,它基本上意味着"在iterable中查找某些项,否则如果没有找到则执行…"。如:
1 2 3 4 5 6 7 | found_obj = None for obj in objects: if obj.key == search_key: found_obj = obj break else: print('No object found.') |
但是,只要您看到这个构造,一个更好的选择就是将搜索封装在一个函数中:
1 2 3 4 | def find_obj(search_key): for obj in objects: if obj.key == search_key: return obj |
或者使用列表理解:
1 2 3 4 5 | 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块。
另请参见摘要了解其他线程
Raymond Hettinger的一篇精彩的演讲题为"将代码转换成漂亮的、惯用的Python",其中他简要介绍了
- Donald Knuth设计了
for ... else 结构,作为某些GOTO 用例的替换; - 重复使用
else 关键字是有意义的,因为"这是knuth使用的,当时人们知道所有的[for 语句]都在下面嵌入了if 和GOTO ,他们期望else ; - 事后看来,它应该被称为"不间断"(或者可能是"不间断"),然后它就不会令人困惑了。
所以,如果问题是,"他们为什么不更改这个关键字?"然后,cat plus可能给出了最准确的答案——此时,它对现有代码的破坏性太大,不实用。但如果你真正问的问题是,为什么一开始使用
就我个人而言,我喜欢在
For completeness, I should mention that with a slight change in
syntax, programmers who want this syntax can have it right now:
1
2
3
4 for item in sequence:
process(item)
else: # no break
suite
视频中那部分的额外引述:"就像我们调用lambda makefunction一样,没有人会问,‘lambda做什么?’"
因为他们不想给语言引入新的关键字。每一个都会窃取一个标识符并导致向后兼容性问题,所以这通常是最后的解决办法。
我发现"得到"for/else所做的,更重要的是,何时使用它,最简单的方法是集中精力在break语句跳到哪里。for/else构造是单个块。break跳出了块,因此跳过了else子句。如果else子句的内容只跟在for子句之后,则不会跳过它,因此必须通过将其放入if来提供等效逻辑。这句话以前说过,但用这些话来说不完全是,所以它可能对其他人有所帮助。尝试运行以下代码片段。我全心全意地支持"不间断"的评论,以便澄清。
1 2 3 4 5 6 7 8 | 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') |
我认为文档对其他方面有很好的解释,继续
[...] it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement."
来源:python 2文档:控制流教程
我读到的内容有:
如果仍然在运行循环的条件下,做一些事情,或者做一些其他的事情。
简单点说,你可以这样想;
- 如果在
for 循环中遇到break 命令,则不会调用else 部分。 - 如果在
for 循环中没有遇到break 命令,将调用else 部分。
换句话说,如果for循环迭代没有与
由于技术部分已经得到了相当多的回答,我的评论只是与产生这个可回收关键字的混乱有关。
由于python是一种非常雄辩的编程语言,关键字的误用更加臭名昭著。
相反,在
问题是,
最后,关键字将保留在python中。很明显,这是错误的,当每个程序员都试图想出一个故事来理解它的用法,就像一些记忆设备一样,这就更清楚了。如果他们选择关键词
它类似于某个孩子在组装玩具的每一步之后所遇到的情况:然后是什么爸爸?
我把它读成"当
我同意,这更像是一个'伊利夫不是[条件提高休息''。
我知道这是一条古老的线索,但我现在正在研究同一个问题,我不确定是否有人以我理解的方式抓住了这个问题的答案。
对我来说,有三种方法可以"阅读"
因此,本质上,循环中的"else"实际上是一个"elif…",其中"…"是(1)no break,相当于(2)not[条件引发break]。
我认为关键是
1 2 3 4 5 | for: do stuff conditional break # implied by else else not break: do more stuff |
因此,
1 2 3 4 5 6 | for: do stuff condition: break else: # read as"else not break" or"else not condition" do more stuff |
正如其他海报所说,当你能找到你的循环所要查找的内容时,通常会出现中断,所以
例子
您还可以同时使用异常处理、中断和for循环。
1 2 3 4 5 6 7 8 9 10 | 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") |
结果
1 2 3 4 5 6 | x: 0 x: 1 x: 2 ASSERTION ERROR: x is 2 ---------- # loop not completed (hit break), so else didn't run |
例子
简单的例子,有一个中断被击中。
1 2 3 4 5 6 7 8 9 | for y in range(0,3): print("y: {}".format(y)) if y == 2: # will be executed print("BREAK: y is {} ----------".format(y)) break else: # not executed because break is hit print("y_loop completed without break---------- ") |
结果
1 2 3 4 5 6 | y: 0 y: 1 y: 2 BREAK: y is 2 ---------- # loop not completed (hit break), so else didn't run |
例子
简单的例子,没有中断,没有引发中断的条件,也没有遇到错误。
1 2 3 4 5 6 7 8 9 10 11 12 | for z in range(0,3): print("z: {}".format(z)) if z == 4: # will not be executed print("BREAK: z is {} ".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 ---------- ") |
结果
1 2 3 4 5 | z: 0 z: 1 z: 2 z_loop complete without break or error ---------- |
在这里,
为了从逻辑上理解
1 2 3 4 5 6 | 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. |
同样,把
1 2 3 4 5 6 | 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 |
区别是
1 2 3 4 | for x in iterable: do_something(x) else: print('no break encountered') # No break statement was encountered |
是的,我知道这种比较可能很困难也很烦人,但它确实澄清了这种混淆。
当
1 2 3 4 5 6 7 | for x in xrange(1,5): if x == 5: print 'find 5' break else: print 'can not find 5!' #can not find 5! |
来自文档:break和continue语句,以及循环上的else子句
Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement. This is exemplified by the following loop, which searches for prime numbers:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 >>> 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(Yes, this is the correct code. Look closely: the else clause belongs to the for loop, not the if statement.)
When used with a loop, the else clause has more in common with the else clause of a try statement than it does that of if statements: a try statement’s else clause runs when no exception occurs, and a loop’s else clause runs when no break occurs. For more on the try statement and exceptions, see Handling Exceptions.
The continue statement, also borrowed from C, continues with the next iteration of the loop:
1
2
3
4
5
6
7
8
9
10
11
12
13 >>> 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
以下是一种思考方法,我没有看到其他人提到过:
首先,记住for循环基本上只是在循环的同时加上句法上的糖分。例如,循环
1 2 | for item in sequence: do_something(item) |
可以重写(大约)为
1 2 3 4 | item = None while sequence.hasnext(): item = sequence.next() do_something(item) |
其次,请记住,虽然循环基本上只是重复if块!您可以将while循环读取为"如果此条件为真,则执行主体,然后返回并再次检查"。
因此,尽管/else有着完美的意义:它的结构与if/else完全相同,具有循环直到条件变为假的附加功能,而不是只检查条件一次。
然后for/else也有很好的意义:因为all for循环只是while循环之上的句法糖分,所以您只需要弄清楚while循环的隐式条件是什么,然后当条件变为false时,其他条件就相应了。
你可以这样想,像其他的东西,或者其他的东西一样,这些不是在循环中完成的。
这是除了搜索之外的另一个惯用用例。假设您希望等待条件为真,例如远程服务器上要打开的端口以及一些超时。然后您可以像这样使用
1 2 3 4 5 6 7 8 9 10 11 12 | 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 2 3 4 5 6 7 8 | for i in range(3): print(i) if i == 2: print("Too big - I'm giving up!") break; else: print("Completed successfully") |
这里的"否则"非常简单,只是意味着
1,"如果
1 2 3 4 5 6 7 8 | 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的性质。
不过,埃多克斯1〔23〕怎么样?
1 2 3 4 5 6 7 8 9 10 | 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 |
所以完全是逻辑组合:
1 2 | if"for clause is completed" or"not run at all": do else stuff |
或者这样说:
1 2 | if"for clause is not partially run": do else stuff |
或者这样:
1 2 | if"for clause not encounter a break": do else stuff |
好的答案是:
- 这解释了历史,以及
- 这就是权利引用以便于您的翻译/理解。
这里我的注释来自唐纳德·克努斯(Donald Knuth)曾经说过的(抱歉找不到引用),即存在一个构造,其中while else与if else不可区分,即(在python中):
1 2 3 4 5 6 | x = 2 while x > 3: print("foo") break else: print("boo") |
具有与以下相同的流量(不包括低液位差):
1 2 3 4 5 | x = 2 if x > 3: print("foo") else: print("boo") |
重点是,如果else可以被看作是语法糖,而else在其
为了减轻你的理解,请这样想:
Without
break ,return , etc., loop ends only when condition is no longer true (infor case you must consider C-stylefor loops or translate them towhile ) andelse block executes when condition is false.
另一个注意事项:
Premature
break ,return , etc. inside loop makes impossible for condition to become false because execution jumped out of the loop while condition was true and would never come back to check it again.
我只是想自己再弄明白一次。我发现以下有帮助!
? ;将
假设我们有一个函数
1 | def broken(x) : return False if x==5 else True |
也就是说只有5个没有坏。现在,如果未使用5对损坏进行评估:
1 2 3 4 | for x in range(4): if not broken(x) : break else: print("Everything broken... doom is upon us") |
将给出输出:
1 | Everything broken... doom is upon us |
其中,当破碎时用5:
1 2 3 4 | for x in range(6): if not broken(x) : break else: print("Everything broken... doom is upon us") |
它不会打印任何东西。因此,间接地告诉至少有一些东西是不坏的。
但是,如果你想欺骗和跳过你发现的坏东西。也就是说,即使您发现5已损坏,仍继续循环,否则语句仍将被打印。即:
1 2 3 4 | for x in range(6): if not broken(x) : continue else: print("Everything broken... doom is upon us") |
将打印
1 | Everything broken... doom is upon us |
我希望它能消除混乱,而不是创造一个新的混乱。
我把这个结构看成是(如果)A或者B,(如果)Else大致上是一个特殊的if-Else。这可能有助于理解其他。
A和B最多执行一次,与if else结构相同。
for(if)可以被视为一个特殊的if,它执行一个循环来尝试满足if条件。一旦满足if条件,a和break;否则,b。