我們都知道 Python 中else的基本用法是在條件控制語句中的 if...elif...else...,但是 else 還有兩個(gè)其它的用途,一是用于循環(huán)的結(jié)尾,另一個(gè)是用在錯(cuò)誤處理的 try 中。這原本是 Python 的標(biāo)準(zhǔn)語法,但由于和大部分其它編程語言的習(xí)慣不太一樣,致使人們有意或無意地忽略了這些用法。另外,對(duì)于這些用法是否符合 0×00 The Zen of Python 的原則以及該不該廣泛使用也存在很多爭議。例如在我看到的兩本書里(Effective Python VS Write Idiomatic Python),兩位作者就分別對(duì)其持有截然不同的態(tài)度。
循環(huán)中的 else
跟在循環(huán)后面的 else 語句只有在當(dāng)循環(huán)內(nèi)沒出現(xiàn) break,也就是正常循環(huán)完成時(shí)才會(huì)執(zhí)行。首先我們來看一個(gè)插入排序法的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from random import randrange def insertion_sort(seq): if len (seq) 1 : return seq _sorted = seq[: 1 ] for i in seq[ 1 :]: inserted = False for j in range ( len (_sorted)): if i _sorted[j]: _sorted = [ * _sorted[:j], i, * _sorted[j:]] inserted = True break if not inserted: _sorted.append(i) return _sorted print (insertion_sort([randrange( 1 , 100 ) for i in range ( 10 )])) [ 8 , 12 , 12 , 34 , 38 , 68 , 72 , 78 , 84 , 90 ] |
在這個(gè)例子中,對(duì)已排序的 _sorted 元素逐個(gè)與 i 進(jìn)行比較,若 i 比已排序的所有元素都大,則只能排在已排序列表的最后。這時(shí)我們就需要一個(gè)額外的狀態(tài)變量 inserted 來標(biāo)記完成遍歷循環(huán)還是中途被 break,在這種情況下,我們可以用 else 來取代這一狀態(tài)變量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
def insertion_sort(seq): if len (seq) 1 : return seq _sorted = seq[: 1 ] for i in seq[ 1 :]: for j in range ( len (_sorted)): if i _sorted[j]: _sorted = [ * _sorted[:j], i, * _sorted[j:]] break else : _sorted.append(i) return _sorted print (insertion_sort([randrange( 1 , 100 ) for i in range ( 10 )])) [ 1 , 10 , 27 , 32 , 32 , 43 , 50 , 55 , 80 , 94 ] |
我認(rèn)為這是一個(gè)非常酷的做法!不過要注意的是,除了 break 可以觸發(fā)后面的 else 語句,沒有循環(huán)的時(shí)候也會(huì):
1
2
3
4
5
6
7
|
while False : print ( "Will never print!" ) else : print ( "Loop failed!" ) Loop failed! |
錯(cuò)誤捕捉中的 else
try...except...else...finally 流程控制語法用于捕捉可能出現(xiàn)的異常并進(jìn)行相應(yīng)的處理,其中 except 用于捕捉 try 語句中出現(xiàn)的錯(cuò)誤;而 else 則用于處理沒有出現(xiàn)錯(cuò)誤的情況;finally 負(fù)責(zé) try 語句的”善后工作“ ,無論如何都會(huì)執(zhí)行。可以通過一個(gè)簡單的例子來展示:
1
2
3
4
5
6
7
8
9
10
11
12
|
def divide(x, y): try : result = x / y except ZeroDivisionError: print ( "division by 0!" ) else : print ( "result = {}" . format (result)) finally : print ( "divide finished!" ) divide( 5 , 2 ) print ( "*" * 20 ) divide( 5 , 0 ) |
1
2
3
4
5
|
result = 2.5 divide finished! * * * * * * * * * * * * * * * * * * * * division by 0 ! divide finished! |
當(dāng)然,也可以用狀態(tài)變量的做法來替代 else:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def divide(x, y): result = None try : result = x / y except ZeroDivisionError: print ( "division by 0!" ) if result is not None : print ( "result = {}" . format (result)) print ( "divide finished!" ) divide( 5 , 2 ) print ( "*" * 20 ) divide( 5 , 0 ) |
1
2
3
4
5
|
result = 2.5 divide finished! * * * * * * * * * * * * * * * * * * * * division by 0 ! divide finished! |
總結(jié)
有人覺得 else 的這些用法違反直覺或者是 implicit 而非 explicit,不值得提倡。但我覺得這種”判決“需要依賴具體的應(yīng)用場景以及我們對(duì) Python 的理解,并非一定要對(duì)新人友好的語法才算是 explicit 的。當(dāng)然也不推薦在所有地方都使用這個(gè)語法,for/while...else 最大的缺點(diǎn)在于 else 是需要與 for/file 對(duì)齊的,如果是多層嵌套或者循環(huán)體太長的情況,就非常不適合用 else(回憶一下游標(biāo)卡尺的梗就知道了:P)。只有在一些簡短的循環(huán)控制語句中,我們通過 else 擺脫一些累贅的狀態(tài)變量,這才是最 Pythonic 的應(yīng)用場景!