Python | Structural Pattern Matching 火力加強版的 Switch
2021-08-16
介紹 Python 3.10 出現的新特色語法 Structural Pattern Matching,示範如何使用 Structural Pattern Matching 讓 Python 能夠寫得更為 Simple & Readability。
說明
以往 Python 一直沒有 swtich,替代的方式是以連續使用 if 或使用 Dict 的 Key 作為 Switch 的 Case:
CLang
switch (score) {
case score > 90:
return 'A';
case score > 80:
return 'B';
default:
return 'C';
}
Python
if score > 90:
return 'A'
elif score > 80:
return 'B'
else:
return 'C'
CLang
switch (monster) {
case "slime":
return "DQ";
case "ratata":
return "Pokemon";
default:
return "Unknown";
}
Python
{
'slime': 'DQ',
'ratata': 'Pokemon'
}.get(monster, 'Unknown')
而根據 Zen of python:
There should be one-- and preferably only one --obvious way to do it.
不需要 switch 使用 if 以及 dict 同樣能夠優美的寫出 switch 的功能。但現在 switch 來了,且不只是 switch 而是 switch 火力加強版的 match (Structural Pattern Matching) 🤔
match 中的 if conditions 稱為 guard
match score:
case x if x > 90:
print('A')
case x if x > 80:
print('B')
case _:
print('C')
match monster:
case 'slime':
print('DQ')
case 'ratata':
print('Pokemon')
case _:
print('Unknown')
而 match (Structural Pattern Matching) 身為火力加強版的 switch 則是多了下列的功能:
Or Patterns
可以讓多個符合的情境有著相同的輸出行為。
match monster:
case 'slime' | 'chimaera' | 'dracky':
print('DQ')
case 'ratata' | 'raticate':
print('Pokemon')
case _:
print('Unknown')
As Patterns
match 可以比對值進行處理,並可以結合 Or Patterns 的使用:
match monster:
case 'slime' | 'chimaera' | 'dracky' as monsterName:
print(f'DQ {monsterName}')
case 'ratata' | 'raticate' as pokeName:
print(f'Pokemon {pokeName}')
case _:
print('Unknown')
此外 Structural Pattern Matching 真正的長處在於對多種資料型別進行比對:
listMatch.py
def listMatch(lst):
match lst:
case ['A']:
return('Only A in the list')
case ['A', x]:
return(f'A and {x} in the list')
case [_]:
return('Only one element in list and it isn\'t A')
case [a, b, *items, c, _]:
return(f'{a} {b} {items} {c}')
print(listMatch(['A']))
# Only A in the list
print(listMatch(['A', 0]))
# A and 0 in the list
print(listMatch(['Z']))
# Only one element in list and it isn't A
print(listMatch([1, 2, 3, 4, 5]))
# 1 2 [3] 4
tupleMatch.py
使用 PEP636 的範例
match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("Not a point")
dictMatch.py
def appwiz(application):
match application:
case {'name': 'SQL Server', **rest}:
print(f'SQL Server, Database Management System, related info: {rest}')
case {'name': 'Chrome', **rest}:
print(f'Chrome, Internet Browser, related info: {rest}')
case {'name': _, **rest}:
print(f'Unknown Application, info: {rest}')
appwiz({'name': 'SQL Server', 'edition': 'Developer'})
# SQL Server, Database Management System, related info: {'edition': 'Developer'}
appwiz({'name': 'Chrome', 'bit': '64'})
# Chrome, Internet Browser, related info: {'bit': '64'}
appwiz({'name': 'Visual Studio'})
# Unknown Application, info: {}
appwiz({})
#
小結
隨著 Python 加入了 match (Structural Pattern Matching),日後對於資料結構的比對值得複雜情境情境,會優先選擇使用 Structural Pattern Matching,以提升程式碼的可讀性以及維護的便利性。