課程節錄
simple matching
-
和傳統我們熟悉的 switch 很接近。
-
wildcard(通配符): 使用 underscore(下劃線)
_
作為「預設」匹配模式。
multiple pattern matching
-
OR(
|
) 模式:一次匹配多個文字case "Java" | "Javascript":
-
capturing matched sub-patterns 捕獲匹配子模式:
case ["move", ("F" | "B" | "L" |"R") as direction]: return symbols[direction]
-
上述語法的簡化:不用每次都打
op(["move", "L"])
,而是連續op(["move", "F", "F", "L"])
case ['move', *directions]: return tuple(symbols[direction] for direction in directions)
-
上述語法在輸入資料不存在時會報錯
op(["move", "up"])
,解決:case ['move', *directions] if set(directions) < symbols.keys(): return tuple(symbols[direction] for direction in directions)
pep 634 後更新 (官網定義)
因已針對 pep 634 更新,上述連結並非 pep 634,而是更新後的說明。
語法
pep 634 語法 & 更新語法說明:只是寫法稍有不同
match_stmt: "match" subject_expr ':' NEWLINE INDENT case_block+ DEDENT
subject_expr:
| star_named_expression ',' star_named_expressions?
| named_expression
case_block: "case" patterns [guard] ':' block
guard: 'if' named_expression
match_stmt ::= 'match' subject_expr ":" NEWLINE INDENT case_block+ DEDENT
subject_expr ::= star_named_expression "," star_named_expressions?
| named_expression
case_block ::= 'case' patterns [guard] ":" block
以下針對上述語法分別說明:
match 語法示意圖
match_stmt ::= 'match' subject_expr ":" NEWLINE INDENT case_block+ DEDENT
subject_expr
subject_expr ::= star_named_expression "," star_named_expressions?
| named_expression
語法說明中的
?
,表示這個部分是可選的(optional),可以有許多個或者沒有。語法說明中的
|
,表示選項中擇一。
這裡的意思是:subject_expr
可以有兩種寫法(|
):
-
star_named_expression “,” star_named_expressions?
star_named_expression
star_named_expression, star_named_expression
star_named_expression, star_named_expression, star_named_expression
-
named_expression
程式示範:
# 定義一個函式,接受兩種不同結構的參數作為主題表達式
def process_data(subject_expr):
match subject_expr:
# 第一種情況:star_named_expression "," star_named_expressions?
case x, *rest:
print("使用 star_named_expression 形式的主題表達式")
print("第一個值:", x)
print("其餘值:", rest)
# 第二種情況:named_expression
case y:
print("使用 named_expression 形式的主題表達式")
print("值:", y)
# 測試函式
# 使用第一種情況:star_named_expression "," star_named_expressions
process_data((1, 2, 3, 4, 5))
# 使用第二種情況:named_expression
process_data("hello")
使用 star_named_expression 形式的主題表達式
第一個值: 1
其餘值: [2, 3, 4, 5]
使用 named_expression 形式的主題表達式
值: hello
case_block
case_block: "case" patterns [guard] ':' block
guard: 'if' named_expression
-
"case"
:這是case_block
的開始關鍵字,它表示了一個匹配情況的開始。 -
patterns
:這是一個模式列表,用於匹配subject_expr
中的值。這些模式可以是任何有效的 Python patterns(模式),包括常數、變數、列表、元組、字典等。 -
[guard]
:這是可選的(optional)保護條件。如果存在保護條件,則在模式匹配成功後,將進一步檢查該保護條件。如果條件為真,則相應的代碼塊將被執行。 -
":"
:這表示case_block
的結束,並標誌著後面的代碼塊的開始。 -
block
:這是與匹配情況相關聯的代碼塊。當模式匹配成功並且保護條件(如果存在)也為真時,將執行這個代碼塊。
def process_data(data):
match data:
# Match constant 匹配常量
case 0:
print("Data is zero")
# Match range 匹配範圍
case 1 | 2 | 5: ## case 1, 2, 5:
print("Data is either 1, 2, or 5")
# Match variable 匹配變量
case x if isinstance(x, int) and x % 2 == 0:
print(f"Data is an even number: {x}")
# Match sequence 匹配序列
case [x, y]:
print(f"Data is a pair: {x}, {y}")
# Match dictionary 匹配字典
case {'name': name, 'age': age}:
print(f"Data is a person with name {name} and age {age}")
# 測試
process_data(0)
process_data(5)
process_data([1, 2])
process_data({'name': 'John', 'age': 30})
process_data(10)
Data is zero
Data is either 1, 2, or 5
Data is a pair: 1, 2
Data is a person with name John and age 30
Data is an even number: 10
patterns
patterns ::= open_sequence_pattern | pattern
pattern ::= as_pattern | or_pattern
closed_pattern ::= | literal_pattern
| capture_pattern
| wildcard_pattern
| value_pattern
| group_pattern
| sequence_pattern
| mapping_pattern
| class_pattern
在上述 patterns 語法中:
-
patterns
:這是一個最上層的規則,表示模式的整體結構。它可以是一個開放的序列模式(open_sequence_pattern)或一個普通模式(pattern)。 -
pattern
:這是一個模式的基本單元,它可以是 作為模式(as_pattern) 或 或模式(or_pattern)。 -
closed_pattern
:這是模式的閉合形式,也就是不能再嵌套更多模式的形式。它可以是空的,或是下列任何一種模式:literal_pattern
:字面值模式,用於精確匹配特定值。capture_pattern
:捕獲模式,用於將變數綁定到匹配部分。wildcard_pattern
:萬用字元模式,表示不關心的值。value_pattern
:值模式,用於對特定類型的值進行匹配。group_pattern
:群組模式,用於將一組模式視為一個單元。sequence_pattern
:序列模式,用於對序列(如列表、元組)進行匹配。mapping_pattern
:映射模式,用於對映射(如字典)進行匹配。class_pattern
:類別模式,用於對類實例進行匹配。
這些模式組合在一起,提供了 Python 中 case 語法中的模式匹配功能。
以下為每一種 pattern 的實際範例:
-
literal_pattern
(字面值模式):這表示一個確切的值。在 case 中,如果變數的值與字面值模式匹配,則該 case 就會被執行。case 42: print("變數值是 42")
-
capture_pattern
(捕獲模式):這允許將變數捕獲為一個名稱,並將其與特定模式匹配的部分綁定在一起。case Point(x, y): print(f"座標是 ({x}, {y})")
-
wildcard_pattern
(萬用字元模式):這是一個省略符號,表示不關心變數的值。case _: print("不管是什麼值")
-
value_pattern
(值模式):這是一個特定的值,用於與變數進行比較。case 0: print("變數值為 0")
-
group_pattern
(群組模式):這允許將一組模式視為一個單元。case (Point(x1, y1), Point(x2, y2)): print(f"座標 1 是 ({x1}, {y1}),座標 2 是 ({x2}, {y2})")
-
sequence_pattern
(序列模式):這表示一個序列(如列表、元組等),並且可以在模式中使用序列解包。case [x, y, z]: print(f"第一個元素是 {x},第二個元素是 {y},第三個元素是 {z}")
-
mapping_pattern
(映射模式):這表示一個映射(如字典),並且可以使用在模式中對鍵值對進行匹配。case {'name': name, 'age': age}: print(f"姓名是 {name},年齡是 {age}")
-
class_pattern
(類別模式):這與捕獲模式類似,但專門用於對類實例進行匹配。case MyClass(value): print(f"類別中的值為 {value}")
這些是 PEP 634 中定義的各種模式,它們讓我們可以更靈活地在 case 語句中進行模式匹配。
pep 636 (官網教學)
GroqChat
Python 3.10 新增 STRUCTURAL PATTERN MATCHING(結構化模式匹配)功能,比傳統的 switch 作法更加強大,具有以下幾個優點:
-
模式(Pattern): 支援更多的模式比較,例如比較列表或字典的元素,或是使用正則表達式等。
-
萬用字元(Wildcards): 支援使用萬用字元 (
_
或*
) 來匹配任意值,可更彈性的模式匹配。 -
嵌套(Nesting): 支援嵌入,可處理 complex data structures。
-
型別(Type): 支援 Type Hints 來限制變數的型別,可以在編譯時就檢查型別的正確性。
-
可讀性(Readability): 相較於 if-elif-else 鏈或 switch-case 語句,structural pattern matching 可以使程式碼更加清晰易讀。
-
可靠性(Reliability): 相較於使用 if-elif-else 鏈或 switch-case 來比較值的相等性,structural pattern matching 可以更可靠地比較值的結構和類型。
-
可擴展性(Extensibility): 使用 Python 的 Type Hints,容易擴展和維護程式碼。
以下就上述優點,分別舉例示範:
from __future__ import annotations
from typing import Any
# 1. 支持更多的模式比較,例如比較列表或字典的元素,或是使用正則表達式等。
def compare_list(value: list[Any]) -> str:
match value:
case [first, *rest] if first == 'a':
return f'List starts with "a" and has {len(rest)} more elements.'
case [first, *rest] if first == 'b':
return f'List starts with "b" and has {len(rest)} more elements.'
case _:
return 'List does not start with "a" or "b".'
# 2. 支持使用萬用字元(Wildcards): 支持使用萬用字元 (_ 或 *) 來匹配任意值,從而實現更加彈性的模式匹配。
def handle_value(value: Any) -> str:
match value:
case int(n) if n % 2 == 0:
return f'Even number: {n}'
case int(n) if n % 2 == 1:
return f'Odd number: {n}'
case str(s) if s.isdigit():
return f'Digit string: {s}'
case str(s):
return f'String: {s}'
case _:
return 'Unknown value'
# 3. 支持嵌套的模式,從而可以更加輕鬆地處理 complex data structures。
def handle_complex_data(value: dict) -> str:
match value:
case {'name': str(name), 'age': int(age)}:
return f'Name: {name}, Age: {age}'
case {'name': str(name), 'address': {'city': str(city), 'state': str(state)}}:
return f'Name: {name}, City: {city}, State: {state}'
case _:
return 'Unknown complex data structure'
# 4. 支持使用 Python 的 Type Hints 來限制變數的型別,從而可以在編譯時就檢查型別的正確性。
def handle_typed_value(value: float) -> str:
match value:
case float(n) if n.is_integer():
return f'Integer: {int(n)}'
case float(n):
return f'Float: {n}'
case _:
return 'Unknown value'
# 5. 可讀性(Readability): 相較於 if-elif-else 鏈或 switch-case 語句,structural pattern matching 可以使程式碼更加清晰易讀。
def handle_readability(value: Any) -> str:
if isinstance(value, int):
if value % 2 == 0:
return f'Even number: {value}'
else:
return f'Odd number: {value}'
elif isinstance(value, str):
if value.isdigit():
return f'Digit string: {value}'
else:
return f'String: {value}'
else:
return 'Unknown value'
# 6. 可靠性(Reliability): 相較於使用 if-elif-else 來比較值的相等性,structural pattern matching 可以更加可靠地比較值的結構和類型。
def handle_reliability(value: Any) -> str:
if isinstance(value, list):
if value[0] == 'a':
return 'List starts with "a".'
else:
return 'List does not start with "a".'
else:
return 'Not a list.'
# 7. 可擴展性(Extensibility): 由於使用了 Python 的 Type Hints,因此可以更加容易地擴展和維護程式碼。
def handle_extensibility(value: dict) -> str:
match value:
case {'name': str(name), 'age': int(age), **rest}:
return f'Name: {name}, Age: {age}, Extra: {rest}'
case _:
return 'Unknown complex data structure'
if __name__ == '__main__':
print(compare_list([1, 2, 3]))
print(compare_list(['a', 2, 3]))
print(compare_list(['b', 2, 3]))
print(handle_value(123))
print(handle_value(12345))
print(handle_value('123'))
print(handle_value('abcd'))
print(handle_complex_data({'name': 'John', 'age': 30}))
print(handle_complex_data({'name': 'Jane', 'address': {'city': 'New York', 'state': 'NY'}}))
print(handle_typed_value(123.0))
print(handle_typed_value(123.4))
print(handle_readability(123))
print(handle_readability('123'))
print(handle_readability('abcd'))
print(handle_reliability([1, 2, 3]))
print(handle_reliability(['a', 2, 3]))
print(handle_reliability('abcd'))
print(handle_extensibility({'name': 'John', 'age': 30}))
print(handle_extensibility({'name': 'Jane', 'age': 30, 'address': '123 Main St'}))