背景
Python 的語法風格確實是懶人福音,真是做到了多寫一行都是罪。就拿 Python-3.8 版本來說吧,為了少寫一行程式碼,直接搞出了一個新的運算子 ` := `。
先給結論!這個運算子不是必要的。也就是說沒有它我們也能寫程式,只是有了它之後我們的程式碼可以更加簡潔。
下面我就來說一下這個運算子的故事!
階段 一
這個階段大家做事都中規中矩,程式碼上直抒胸臆。給個例子吧。假設我們有一個函式,它是用來處理列表的,它會在處理之前檢查一下列表的長度,當長度大於 7 時直接結束。階段一這個時候的程式碼看起來如下。
deffun(lst=None):
"""
Parameters:
lst: 列表
Return:
None
"""
#
if len(lst) > 7:
print(f"len(lst) = {len(lst)} gt 7 , not supported .")
return
# 其它邏輯
這裏有一個小小的問題, 就是 `len(lst)` 在命中 if 的語句時候它還會在 print 語句裏面再被計算一次 。如果計算本身的開銷就比較高,少計算一次就非常有吸重力了,階段二就是向這個方向前進演化的。
階段二
階段二的寫法也是非常直接,只要保存一下第一次計算的結果,第二次的時候直接取結果,這樣就不用再計算一次了。上程式碼
deffun(lst=None):
"""
Parameters:
lst: 列表
Return:
None
"""
#
n = len(lst)
if n > 7:
print(f"len(lst) = {n} gt 7 , not supported .")
return
# 其它邏輯
階段二的寫法也不是完全沒有可以改進的地方, 我們看 n 其實只在 if 塊裏面有用到,但是它的聲明位置是在 if 之外的。這個就給人一種,這個 n 非常重要後面的程式碼還會用到它的感覺。也就是說這種寫法沒有辦法表現出 n 就是一個臨時變量 。
好在這種語意已經可以在 Python-3.8 這個版本中表達了,不過我們要借助全新的運算子 `
:=
` 來實作。 詳細的請看階段三。
階段三
階段三是真正的做到了形與意合,並且沒有什麽學習成本,語法上可以說是一看就懂,我直接上程式碼。
deffun(lst=None):
"""
Parameters:
lst: 列表
Return:
None
"""
#
if (n:= len(lst)) > 7:
print(f"len(lst) = {n} gt 7 , not supported .")
return
# 其它邏輯
現在從詞法上看,就能非常明確地知道 n 只在 if 語句內起作用。由於階段三只是階段二的語法糖,也就是說從作用域上來講 n 在 if 語句之後還是可以正常存取,這個應該就是唯一美中不足的地方了吧。
為什麽說 := 是語法糖
這一點是從官方文件上不能直接看出來的,需要我們去看 fun 函式兩種不同寫法,編譯出來的字節碼。
In[1]: import dis
In[2]: def fun(lst=None):
...: """
...: Parameters:
...: lst: 列表
...:
...: Return:
...: None
...: """
...: #
...:
...: if (n:= len(lst)) > 7:
...: print(f"len(lst) = {n} gt 7 , not supported .")
...: return
...:
In[3]: dis.dis(fun)
10 RESUME 0
112 LOAD_GLOBAL 1 (NULL + len)
14LOAD_FAST 0 (lst)
16PRECALL 1
20CALL 1
30COPY 1
32STORE_FAST 1 (n)
34LOAD_CONST 1 (7)
36COMPARE_OP 4 (>)
42POP_JUMP_FORWARD_IF_FALSE 21 (to 86)
1244 LOAD_GLOBAL 3 (NULL + print)
56LOAD_CONST 2 ('len(lst) = ')
58LOAD_FAST 1 (n)
60FORMAT_VALUE 0
62LOAD_CONST 3 (' gt 7 , not supported .')
64BUILD_STRING 3
66PRECALL 1
70CALL 1
80POP_TOP
1382 LOAD_CONST 4 (None)
84RETURN_VALUE
11>> 86 LOAD_CONST 4 (None)
88RETURN_VALUE
In[4]: def fun(lst=None):
...: """
...: Parameters:
...: lst: 列表
...:
...: Return:
...: None
...: """
...: #
...: n = len(lst)
...: if n > 7:
...: print(f"len(lst) = {n} gt 7 , not supported .")
...: return
...:
In[5]: dis.dis(fun)
10 RESUME 0
102 LOAD_GLOBAL 1 (NULL + len)
14LOAD_FAST 0 (lst)
16PRECALL 1
20CALL 1
30STORE_FAST 1 (n)
1132 LOAD_FAST 1 (n)
34LOAD_CONST 1 (7)
36COMPARE_OP 4 (>)
42POP_JUMP_FORWARD_IF_FALSE 21 (to 86)
1244 LOAD_GLOBAL 3 (NULL + print)
56LOAD_CONST 2 ('len(lst) = ')
58LOAD_FAST 1 (n)
60FORMAT_VALUE 0
62LOAD_CONST 3 (' gt 7 , not supported .')
64BUILD_STRING 3
66PRECALL 1
70CALL 1
80POP_TOP
1382 LOAD_CONST 4 (None)
84RETURN_VALUE
11>> 86 LOAD_CONST 4 (None)
88RETURN_VALUE
可以看到階段三和階段二的字節碼是一模一樣的。
以上是今天的分享,最後推薦一下我的【Python潮流周刊】專欄。試執行付費模式,目前最低價,即將漲價!歡迎訂閱。