增加了采用模式加上相應動作的?match 語句?和?case 語句?的形式的結構化模式匹配。 模式由序列、映射、基本數據類型以及類實例構成。 模式匹配使得程序能夠從復雜的數據類型中提取信息、根據數據結構實現分支,并基于不同的數據形式應用特定的動作。
語法與操作
模式匹配的通用語法如下:
match subject:case <pattern_1>:<action_1>case <pattern_2>:<action_2>case <pattern_3>:<action_3>case _:<action_wildcard>
match 語句接受一個表達式并將其值與以一個或多個 case 語句塊形式給出的一系列模式進行比較。 具體來說,模式匹配的操作如下:
-
使用具有特定類型和形狀的數據 (
subject
) -
針對?
subject
?在?match
?語句中求值 -
從上到下對 subject 與?
case
?語句中的每個模式進行比較直到確認匹配到一個模式。 -
執行與被確認匹配的模式相關聯的動作。
-
如果沒有確認到一個完全的匹配,則如果提供了使用通配符?
_
?的最后一個 case 語句,則它將被用作已匹配模式。 如果沒有確認到一個完全的匹配并且不存在使用通配符的 case 語句,則整個 match 代碼塊不執行任何操作。
聲明性方式
讀者可能是通過 C, Java 或 JavaScript (以及其他許多語言) 中的?switch?語句將一個目標 (數據對象) 與一個字面值 (模式) 進行匹配的簡單例子了解到模式匹配的概念的。?switch?語句常常被用來將一個對象/表達式與包含在 case 語句中的字面值進行比較。
更強大的模式匹配例子可以在 Scala 和 Elixir 等語言中找到。 這種結構化模式匹配方式是“聲明性”的并且會顯式地為所要匹配的數據指定條件(模式)。
雖然使用嵌套的“if”語句的“命令性”系列指令可以被用來完成類似結構化模式匹配的效果,但它沒有“聲明性”方式那樣清晰。 相反地,“聲明性”方式指定了一個匹配所要滿足的條件,并且通過其顯式的模式使之更為易讀。 雖然結構化模式匹配可以采取將一個變量與一個 case 語句中的字面值進行比較的最簡單形式來使用,但它對于 Python 的真正價值在于其針對目標類型和形狀的處理操作。
簡單模式:匹配一個字面值
讓我們把這個例子看作是模式匹配的最簡單形式:一個值,即主詞,被匹配到幾個字面值,即模式。在下面的例子中,status
?是匹配語句的主詞。模式是每個 case 語句,字面值代表請求狀態代碼。匹配后,將執行與該 case 相關的動作:
注:case后面跟變量,與case _是一樣的效果
def http_error(status):match status:case 400:return "Bad request"case 404:return "Not found"case 418:return "I'm a teapot"case _:return "Something's wrong with the internet"
如果傳給上述函數的?status
?為 418,則會返回 "I'm a teapot"。 如果傳給上述函數的?status
?為 500,則帶有?_
?的 case 語句將作為通配符匹配,并會返回 "Something's wrong with the internet"。 請注意最后一個代碼塊:變量名?_
?將作為?通配符?并確保目標將總是被匹配。?_
?的使用是可選的。
你可以使用?|
?(“ or ”)在一個模式中組合幾個字面值:
case 401 | 403 | 404:return "Not allowed"
無通配符的行為
如果我們修改上面的例子,去掉最后一個 case 塊,這個例子就變成:
def http_error(status):match status:case 400:return "Bad request"case 404:return "Not found"case 418:return "I'm a teapot"
如果不在 case 語句中使用?_
,可能會出現不存在匹配的情況。如果不存在匹配,則行為是一個 no-op。例如,如果傳入了值為 500 的?status
?,就會發生 no-op。
帶有字面值和變量的模式
模式可以看起來像解包形式,而且模式可以用來綁定變量。在這個例子中,一個數據點可以被解包為它的 x 坐標和 y 坐標:
# point is an (x, y) tuple 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")
第一個模式有兩個字面值?(0,?0)
?,可以看作是上面所示字面值模式的擴展。接下來的兩個模式結合了一個字面值和一個變量,而變量?綁定?了一個來自主詞的值(point
)。 第四種模式捕獲了兩個值,這使得它在概念上類似于解包賦值?(x,?y)?=?point
?。
as關鍵字使用別名
set=(3,7) match set:case(1,3):print("1 or 3")case ((3,7) as x):print(f"{x}")case _:print("other")
?
匹配模型也可以使用數據類型來匹配
list01 = [1, 2, 3, 4] list02 = (10, 2) match list02:#模式匹配可以在case后面使用if條件語句,也可以使用as關鍵字來給變量賦值case tuple([a,b]) if a>3:print("list01 is a tuple")case list([x,y]):#數據類型+元素數量print("list01 is a list")case set():print("list01 is a set")case _: # 這里的 _ 代表其他任何類型print("list01 is not a tuple or a list")
匹配模式可解包
# 定義一個列表
list01 = [1, 2, 3, 4, 5, 6]# 使用模式匹配語法進行匹配
match list01:
? ? # 如果列表中至少有兩個元素,將前兩個元素分別賦值給變量x和y,剩余的元素賦值給變量z
? ? case [x, y, *z]:
? ? ? ? print(f"x={x}, y={y}, rest={z}")
? ? # 如果列表中的元素不滿足前面的模式,則執行這個默認情況
? ? case _:
? ? ? ? print("Invalid pattern")
?
case [x, y, *z]
:這個模式匹配語句檢查列表是否至少有兩個元素,如果是,它會將前兩個元素分別賦值給變量x
和y
,剩余的元素賦值給變量z
。case _
:這個是默認情況,匹配所有其它情況。
在這個例子中,由于列表list01
有至少兩個元素,因此第一個模式被匹配。x
被賦值為1,y
被賦值為2,剩余的元素3、4、5和6被賦值給變量z
。所以,最后輸出的結果是:x=1, y=2, rest=[3, 4, 5, 6]
。
?