match-case 语句

match-case 语句

概述

Python 3.10 引入了 match-case 语句,这是一种强大的结构匹配模式,类似于其他语言中的 switch-case 语句,但功能更加强大和灵活。match-case 不仅可以进行简单的值匹配,还支持复杂的模式匹配,包括序列匹配、字典匹配、类型匹配等。

学习目标

通过本章学习,你将掌握:

  • match-case 语句的基本语法
  • 标量匹配和多值匹配
  • 序列和字典的模式匹配
  • 类型匹配和自定义类匹配
  • 通配符和变量绑定的使用
  • match-case 的高级特性

前置知识

  • Python 基础语法
  • 数据类型(列表、元组、字典等)
  • 类和对象的基本概念

详细内容

基本语法

match subject:
    case <pattern_1>:
        <action_1>
    case <pattern_2>:
        <action_2>
    case <pattern_3>:
        <action_3>
    case _:
        <action_wildcard>

case _: 类似于其他语言中的 default:,当其他 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"

print(http_error(404))  # 输出: Not found

多值匹配

一个 case 可以匹配多个值,使用 | 分隔:

def check_status(status):
    match status:
        case 401 | 403 | 404:
            return "Not allowed"
        case 500 | 502 | 503:
            return "Server error"
        case _:
            return "Unknown status"

print(check_status(403))  # 输出: Not allowed

序列匹配

元组和列表匹配

def analyze_point(point):
    match point:
        case (0, 0):
            print("坐标原点")
        case (0, y):
            print(f"Y 轴上的点,Y={y}")
        case (x, 0):
            print(f"X 轴上的点,X={x}")
        case (x, y) if x > 0 and y > 0:
            print(f"第一象限的点:({x}, {y})")
        case (x, y):
            print(f"其他象限的点:({x}, {y})")
        case _:
            print("不是二维坐标点")

analyze_point((0, 0))    # 坐标原点
analyze_point((3, 4))    # 第一象限的点:(3, 4)
analyze_point([0, 5])    # Y 轴上的点,Y=5

通配符使用

def process_sequence(data):
    match data:
        case [x, *rest]:  # 匹配至少有一个元素的列表
            print(f"第一个元素: {x}, 其余元素: {rest}")
        case [x, y, *_]:  # 匹配至少有两个元素的列表
            print(f"前两个元素: {x}, {y}")
        case []:
            print("空列表")
        case _:
            print("其他类型")

process_sequence([1, 2, 3, 4])  # 第一个元素: 1, 其余元素: [2, 3, 4]
process_sequence([])            # 空列表

字典匹配

字典匹配只需要包含指定的键值对即可,可以有额外的键:

def process_user(user_data):
    match user_data:
        case {'name': name, 'age': age} if age >= 18:
            print(f"成年用户: {name}, 年龄: {age}")
        case {'name': name, 'age': age}:
            print(f"未成年用户: {name}, 年龄: {age}")
        case {'name': name, **rest}:
            print(f"用户: {name}, 其他信息: {rest}")
        case _:
            print("无效的用户数据")

process_user({'name': 'Alice', 'age': 25, 'city': 'Beijing'})
## 输出: 成年用户: Alice, 年龄: 25

类型匹配

def process_data(data):
    match data:
        case int() if data > 0:
            print(f"正整数: {data}")
        case int():
            print(f"非正整数: {data}")
        case str() if len(data) > 0:
            print(f"非空字符串: {data}")
        case list() as items:
            print(f"列表,包含 {len(items)} 个元素")
        case dict() as mapping:
            print(f"字典,包含 {len(mapping)} 个键")
        case _:
            print(f"其他类型: {type(data)}")

process_data(42)        # 正整数: 42
process_data("hello")   # 非空字符串: hello
process_data([1, 2, 3]) # 列表,包含 3 个元素

自定义类匹配

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Circle:
    def __init__(self, center, radius):
        self.center = center
        self.radius = radius

def analyze_shape(shape):
    match shape:
        case Point(x=0, y=0):
            print("原点")
        case Point(x=x, y=y):
            print(f"点坐标: ({x}, {y})")
        case Circle(center=Point(x=0, y=0), radius=r):
            print(f"以原点为圆心,半径为 {r} 的圆")
        case Circle(center=center, radius=r):
            print(f"圆心在 ({center.x}, {center.y}),半径为 {r} 的圆")
        case _:
            print("未知形状")

analyze_shape(Point(3, 4))  # 点坐标: (3, 4)
analyze_shape(Circle(Point(0, 0), 5))  # 以原点为圆心,半径为 5 的圆

AS 模式

AS 模式用于在匹配的同时进行变量绑定:

def process_nested_data(data):
    match data:
        case {'user': {'name': str() as name, 'age': int() as age}}:
            print(f"用户信息 - 姓名: {name}, 年龄: {age}")
        case [int() as first, *rest] if len(rest) > 0:
            print(f"数字列表 - 第一个: {first}, 其余: {rest}")
        case str() as text if len(text) > 10:
            print(f"长字符串: {text[:10]}...")
        case _:
            print("其他数据")

process_nested_data({'user': {'name': 'Bob', 'age': 30}})
## 输出: 用户信息 - 姓名: Bob, 年龄: 30

条件匹配

可以在 case 后添加 if 条件:

def grade_score(score):
    match score:
        case x if x >= 90:
            return "优秀"
        case x if x >= 80:
            return "良好"
        case x if x >= 70:
            return "中等"
        case x if x >= 60:
            return "及格"
        case _:
            return "不及格"

print(grade_score(85))  # 输出: 良好

实际应用场景

1. 命令行参数处理

def handle_command(command):
    match command:
        case ['help']:
            print("显示帮助信息")
        case ['list', '--all']:
            print("列出所有项目")
        case ['list', category]:
            print(f"列出 {category} 类别的项目")
        case ['create', name, '--type', type_name]:
            print(f"创建类型为 {type_name}{name}")
        case ['delete', name] if name != 'important':
            print(f"删除 {name}")
        case _:
            print("未知命令")

handle_command(['create', 'myproject', '--type', 'web'])
## 输出: 创建类型为 web 的 myproject

2. JSON 数据处理

def process_api_response(response):
    match response:
        case {'status': 'success', 'data': data}:
            return f"成功获取数据: {len(data)} 条记录"
        case {'status': 'error', 'message': msg, 'code': code}:
            return f"错误 {code}: {msg}"
        case {'status': 'pending', 'progress': progress}:
            return f"处理中... {progress}%"
        case _:
            return "未知响应格式"

response1 = {'status': 'success', 'data': [1, 2, 3]}
print(process_api_response(response1))  # 成功获取数据: 3 条记录

3. 状态机实现

class StateMachine:
    def __init__(self):
        self.state = 'idle'
        self.data = None
    
    def process_event(self, event):
        match (self.state, event):
            case ('idle', 'start'):
                self.state = 'running'
                print("状态机启动")
            case ('running', 'pause'):
                self.state = 'paused'
                print("状态机暂停")
            case ('paused', 'resume'):
                self.state = 'running'
                print("状态机恢复")
            case ('running' | 'paused', 'stop'):
                self.state = 'idle'
                print("状态机停止")
            case _:
                print(f"无效的状态转换: {self.state} -> {event}")

sm = StateMachine()
sm.process_event('start')   # 状态机启动
sm.process_event('pause')   # 状态机暂停
sm.process_event('resume')  # 状态机恢复

常见陷阱与最佳实践

1. 变量绑定注意事项

## 错误:变量不能作为匹配模式
## error_code = 500
## match status:
##     case error_code:  # 这会报错
##         return "Server Error"

## 正确:使用常量或条件
match status:
    case x if x == 500:
        return "Server Error"

2. 匹配顺序很重要

def check_number(x):
    match x:
        case n if n > 0:
            return "正数"
        case 0:
            return "零"  # 这个永远不会被执行到
        case n if n < 0:
            return "负数"

## 正确的顺序
def check_number_correct(x):
    match x:
        case 0:
            return "零"
        case n if n > 0:
            return "正数"
        case n if n < 0:
            return "负数"

3. 性能考虑

## 对于简单的值匹配,match-case 比 if-elif 更高效
def handle_status_match(status):
    match status:
        case 200: return "OK"
        case 404: return "Not Found"
        case 500: return "Server Error"
        case _: return "Unknown"

## 但对于复杂条件,if-elif 可能更清晰
def handle_complex_condition(data):
    if isinstance(data, dict) and 'user' in data and data['user'].get('active'):
        return "Active user"
    elif isinstance(data, list) and len(data) > 10:
        return "Large list"
    else:
        return "Other"

相关函数与模块

内置函数

  • isinstance() - 类型检查
  • hasattr() - 属性检查
  • callable() - 可调用对象检查

标准库模块

  • enum - 枚举类型,常与 match-case 配合使用
  • dataclasses - 数据类,便于模式匹配
  • typing - 类型提示,增强匹配的可读性

第三方库

扩展阅读

相关标签

Python 控制流 模式匹配 条件语句 结构化匹配 Python3.10 语法糖

讨论与反馈

欢迎在下方留言讨论,分享你的学习心得或提出问题。评论基于GitHub Issues,需要GitHub账号。