海象運算符
這個新運算符 :=
能讓我們為表達式中的一個變量賦值,這個符號看起來頗有些類似于海象的眼睛和犬齒。
我們先來看看下面一段代碼:
countries = [“India”, “USA”, “France”, “Germany”]
if len(countries) < 5:
print ("Length of countries is " + len(countries))
在上面的代碼段中,我們兩次調用了函數 len()。我們可以避免重復計算以提升可讀性嗎?當然可以,我們可對這段代碼進行如下改進:
country_size = len(countries)
if country_size < 5:
print ("Length of countries is " + country_size)
還有進一步改進的空間嗎?我們可以不用單獨一行來給「country_size」賦值嗎?
if country_size := len(countries) < 5 :
print ("Length of countries is " + country_size)
這就是 Python 3.8 引入的海象運算符的用武之地。我們可以在 if 語句之中直接執行聲明和賦值操作。我們下面進一步探索該運算符的能力。
代碼行數與復雜度的平衡
先看看以下示例(多次調用一個高成本的函數)
powers = [get_count(), get_count()**2, get_count()**3]
def get_count():
"Fetches count of records from a database"
上面的示例是通過多次調用一個高成本的函數 get_count() 來填充一個列表。
有了海象運算符的幫助,我們可以避免多次調用函數 get_count(),其具體的功能是將結果存儲到一個變量中,然后我們可在后續的計算中復用同一個變量。
下面演示了海象運算符的用法:
powers =[result:= get_count(), result**2, result**3]
def get_count():
"Fetches count of records from a database"
從上面的例子可以看到,海象運算符可以減少代碼行數,讓代碼更可讀,因此能簡化代碼審查人員的工作。此外,這也能實現代碼行數和代碼復雜度的平衡。
解決理解低效的問題
employees = []
for id in employee_ids:
employee = fetch_employee(id)
if employee:
employees.append(employee)
基于一個條件填充列表
上面的示例需要多次執行循環。一開始,我們創建一個空列表,然后在 id 列表上迭代并通過檢查結果是否有效來填充我們創建的列表。
我們可以簡化上面的代碼,將其濃縮為一行:
employees = [result for id in employee_ids if (result:= fetch_employee(id))]
# 使用海象運算符避免低效理解
文件分塊處理
在處理大文件時,我們會將文件分塊讀取。每當讀取一個分塊時,都會檢查它的值,并且該值也是 while 循環的終止條件。
chunk = file.read(256)
while chunk:
process(chunk)
chunk = file.read(256)
我們可以在 while 循環表達式中讀取數據以及為要讀取的數據賦值。由此我們就能避免在 while 循環之外顯式地聲明變量。如下示例:
while chunk := file.read(256) :
process(chunk)
正則表達式匹配
正則表達式匹配是一個兩步式過程。第一步是檢查是否有匹配,第二步是提取匹配的部分。
obj = re.match(info).group(1) if re.match(info) else None
#正則表達式匹配
從上面的代碼可以觀察到,我們在一次匹配中重復計算了 re.match(info)。這會減慢該程序的執行速度,而且數據量越大減慢得越明顯。上面的代碼可以重寫為如下形式,從而避免重復計算:
obj = match.group(1) if match:= re.match(info) else None
#使用 := 的正則表達式匹配
不能使用海象運算符的地方
為變量賦值
a = 5 # 有效
a := 5 # 無效
empty_list = [] # 有效
empty_list := [] # 無效
如上所示,我們不能使用 := 替代 =。海象運算符只能是一個表達式的一部分。
加法/減法賦值
a += 5 # 有效
a :+=5 # 無效
在 lambda 函數中為表達式賦值
(lambda: a:= 5) # 無效
lambda: (a := 5) # 有效但無用
(var := lambda: 5) # 有效
PEP-572 及其爭議
海象預算符的爭議點有很多,下面是其中幾個:
句法變化問題:開發者們為 :=
提議了多種替代方案,比如「表達式 -> NAME
」、「NAME -> 表達式
」、「{表達式} NAME
」等等。少數人建議使用現有的關鍵字,其他人則使用了新的運算符。
后向兼容問題:這個特性無法向后兼容,也無法運行在之前的 Python 版本上。
運算符名稱問題:人們建議不要使用「海象運算符」這樣的代號,而是使用「賦值運算符」、「命名表達式運算符」、「成為運算符」等術語,以免人們不明白。