match (1, 2, 3): case (1, _, _): # 匹配第一个元素是1的长度为3的元组 print("Starts with 1") case (_, 2, _): # 匹配第二个元素是2的长度为3的元组 print("Second element is 2") # 这里不会执行,因为第一个匹配已经被接受 case _: print("Fallback")
# Output: Starts with 1
3.3 3. 捕获模式 (Capture Patterns)
通过一个变量名来匹配任何值,并将匹配到的值绑定到该变量上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
defprocess_message(message): match message: case ["LOG", level, text]: # 匹配列表,将第二个元素绑定到 level,第三个绑定到 text print(f"[{level.upper()}] {text}") case ["ERROR", code]: print(f"Error occurred: {code}") case ["ALERT", msg] if"urgent"in msg.lower(): # 带有 guard 的捕获模式 print(f"!!! URGENT ALERT: {msg} !!!") case cmd: # 捕获任何不匹配其他模式的值到 cmd 变量 print(f"Unrecognized command: {cmd}")
process_message(["LOG", "info", "User logged in"]) # Output: [INFO] User logged in process_message(["ERROR", 503]) # Output: Error occurred: 503 process_message(["ALERT", "System reboot initiated (urgent)"]) # Output: !!! URGENT ALERT: System reboot initiated (urgent) !!! process_message("Just text") # Output: Unrecognized command: Just text
3.4 4. 序列模式 (Sequence Patterns)
匹配列表或元组等序列类型。可以指定长度、元素值以及使用捕获模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
defprocess_data(data): match data: case [item1, item2]: # 匹配长度为2的序列,并捕获两个元素 print(f"Two items: {item1} and {item2}") case [head, *tail]: # 匹配至少一个元素的序列,捕获第一个元素和其余部分 print(f"Head: {head}, Tail: {tail}") case []: # 匹配空序列 print("Empty list/tuple") case _: print("Not a sequence or unsupported sequence.")
process_data([10, 20]) # Output: Two items: 10 and 20 process_data(("a", "b", "c")) # Output: Head: a, Tail: ('b', 'c') process_data([]) # Output: Empty list/tuple process_data("hello") # Output: Not a sequence or unsupported sequence. (字符串也是序列,但此处为示例)
注意:*name 只能在序列模式中出现一次,且必须是可迭代的尾部。
3.5 5. 映射模式 (Mapping Patterns)
匹配字典类型。可以检查键是否存在,并捕获对应的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
defhandle_event(event): match event: case {"type": "click", "x": x, "y": y}: # 匹配 type 为 "click" 且包含 x, y 键的字典 print(f"Click at ({x}, {y})") case {"type": "login", "user": user, "timestamp": ts}: print(f"User '{user}' logged in at {ts}") case {"type": "error", "message": msg, **rest}: # 捕获其余的键值对到 rest print(f"Error: {msg}, Details: {rest}") case _: print(f"Unknown event: {event}")
defidentify_shape(shape): match shape: case Point(x=0, y=0): # 匹配 x=0 且 y=0 的 Point 对象 print("Origin point") case Point(x=x, y=y) if x == y: # 匹配 x=y 的 Point 对象,并捕获 x, y print(f"Point on diagonal: ({x}, {y})") case Point(x, y): # 匹配任意 Point 对象,并按顺序捕获属性 x, y print(f"Any point at ({x}, {y})") case Circle(center=Point(0, 0), radius=r): # 嵌套模式:匹配中心是原点的 Circle print(f"Circle centered at origin with radius {r}") case Circle(Point(cx, cy), r): # 匹配任意 Circle 对象,并解构其属性 print(f"Circle at ({cx}, {cy}) with radius {r}") case _: print("Unknown shape")
identify_shape(Point(0, 0)) # Output: Origin point identify_shape(Point(5, 5)) # Output: Point on diagonal: (5, 5) identify_shape(Point(10, 20)) # Output: Any point at (10, 20) identify_shape(Circle(Point(0, 0), 10)) # Output: Circle centered at origin with radius 10 identify_shape(Circle(Point(3, 4), 5)) # Output: Circle at (3, 4) with radius 5 identify_shape("not a shape") # Output: Unknown shape
类模式的匹配规则:
case ClassName(arg1, arg2, ...):位置参数模式。如果类定义了 __match_args__ 属性,那么 arg1 等将按顺序匹配 __match_args__ 中指定的属性。
case ClassName(attr=value, ...):关键字参数模式。直接匹配对象的属性。
get_color_type("red") # Output: Primary color get_color_type("orange") # Output: Secondary color get_color_type("black") # Output: Other color
3.8 8. AS 模式 (as)
在匹配成功后,将整个匹配到的值绑定到一个变量上,同时也可以进行模式匹配。
1 2 3 4 5 6 7 8 9 10 11
defprocess_point(coord): match coord: case (int() as x, int() as y) if x == y: # 匹配两个整数,且相等,同时捕获到 x, y print(f"Point on diagonal: ({x}, {y})") case Point(x, y) as p: # 匹配 Point 对象,捕获其 x, y 属性,并将整个对象捕获到 p print(f"Matched Point object: {p} (x={x}, y={y})") case _: print("Not a matching point.")
process_point((5, 5)) # Output: Point on diagonal: (5, 5) process_point(Point(1, 2)) # Output: Matched Point object: Point(1, 2) (x=1, y=2)
3.9 9. Guard (守卫) - if 子句
在 case 模式后面可以添加一个 if 子句,作为额外的条件。只有当模式匹配成功且if 条件为真时,该 case 块才会被执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
defhandle_num(value): match value: caseint() if value > 0: print(f"Positive integer: {value}") caseint() if value < 0: print(f"Negative integer: {value}") case0: print("Zero") case _: print(f"Non-integer value: {value}")
defexecute_command(command_tuple): match command_tuple: case ("quit",): print("Exiting application.") returnTrue case ("load", filename): print(f"Loading file: {filename}") case ("move", x, y) ifisinstance(x, int) andisinstance(y, int): print(f"Moving to ({x}, {y})") case ("update", key, value): print(f"Updating {key} to {value}") case ("help", cmd_name): print(f"Displaying help for '{cmd_name}'.") case ("help",): print("Displaying general help.") case _: print(f"Unknown command: {command_tuple}") returnFalse
classColor(Enum): RED = 1 GREEN = 2 BLUE = 3 YELLOW = 4
defget_color_category(color_enum): match color_enum: case Color.RED | Color.GREEN | Color.BLUE: print("Primary Color") case Color.YELLOW: print("Secondary Color") case _: print("Unknown Color")
get_color_category(Color.RED) # Output: Primary Color get_color_category(Color.YELLOW) # Output: Secondary Color
五、模式匹配的设计哲学与注意事项
5.1 1. 匹配的是结构和值,而不是任意表达式
case 后面的模式不是任意的 Python 表达式。它们是专门的模式语法,用于匹配并解构数据。
1 2 3 4 5
x = 10 match20: case x: # WARNING: 这不是匹配 subject == x,而是将 20 捕获到 x 变量! # 这会覆盖外部的 x 变量,且 x 总是匹配成功。 print(f"Matched {x}") # Output: Matched 20
正确做法:如果想匹配一个变量的值,需要使用 if 守卫或者将变量作为字面量表达式(例如使用常量)。
1 2 3 4 5 6 7 8 9 10
x = 10 match20: case val if val == x: # 使用 if 守卫 print(f"Matched {val} with x") case _: print("Did not match x")