Luke Hande 2022-05-09
Python 编程语言持续不断地发展,每次更新都会引入新特性和功能。Python 3.10 于 2021 年年中发布,带来了结构化模式匹配(structural pattern matching),也称为 match-case 语句。这是 Python 3.10 最重要的新特性;该功能让你能够更轻松地控制程序流程——当满足特定条件(或“情况”)时,执行相应的代码块。
在本文中,我们将全面介绍 Python 中的 match-case 语句,帮助你实现对程序执行流程的精细控制。
关于 Python 版本的说明
在开始之前,我们先谈谈 Python 版本和基础知识。
如果你当前使用的是旧版本的 Python,我们建议你升级到 Python 3.10,以便充分利用本文内容。这样你就可以直接运行文中的代码片段,并根据自己的项目需求进行修改。
你可以访问 Python 官方下载页面 获取最新版本。如果你想深入了解 match-case 语句,可以查阅三个新的 Python 增强提案(PEPs):
- PEP 636:模式匹配的入门教程(推荐起点)
- PEP 634:详细规范说明
- PEP 635:该特性的设计动机与原理
好了,现在让我们深入探讨 Python 的 match-case 语句。
Python 中的 match-case 语句
match-case 语句的基本用法看起来很像 Python 中的 if 语句。
对于熟悉 Java 或 C 等其他语言的开发者来说,match-case 可能类似于 switch 语句。switch 语句在功能上与 if-else 类似,但在定义多个分支时代码更简洁。
而 Python 的 match-case 语句更加强大,支持复杂的模式匹配。我们先看一个基本示例来了解其语法:
>>> command = 'Hello, World!'
>>> match command:
... case 'Hello, World!':
... print('Hello to you too!')
... case 'Goodbye, World!':
... print('See you later')
... case other:
... print('No match found')
...
Hello to you too!
这里我们定义了一个变量 command,并使用 match 关键字将其与后续每个 case 定义的模式进行匹配。
小提示:
match和case是所谓的“软关键字”(soft keywords),仅在 match-case 语句中具有特殊含义。你仍然可以在程序其他地方将它们用作普通变量名。
最后的 case other 相当于 if-elif-else 中的 else,也可以简写为 case _(下划线 _ 是通配符,表示“任意值”)。
上面的例子中我们使用了 print() 函数输出文本,但实际上任何命令或函数调用都可以放在 case 块中执行。下面我们会看到更复杂的例子。
为什么要使用 match-case 语句?
上面的例子完全可以用 if-elif-else 实现。但在本节中,我们将通过两个更复杂的例子展示 match-case 如何简化流程控制,使代码更清晰、更不易出错。
假设我们要编写一个脚本来处理大量文件,可以这样写:
>>> def file_handler_v1(command):
... match command.split():
... case ['show']:
... print('List all files and directories: ')
... # 列出文件和目录的代码
... case ['remove', *files]:
... print('Removing files: {}'.format(files))
... # 删除文件的代码
... case _:
... print('Command not recognized')
输入仍然是一个字符串,通过 command.split() 按空格分割成字符串列表。
- 第一个 case 在输入为
'show'时匹配(split()返回['show']),然后执行列出文件的逻辑(此处用注释代替实际代码,实际可使用os模块实现)。 - 第二个 case 更有趣。例如:
>>> file_handler_v1('remove file1.txt file2.jpg file3.pdf')
Removing files: ['file1.txt', 'file2.jpg', 'file3.pdf']
这里 'remove' 被匹配,而 *files 是一个星号表达式(类似 *args),它会捕获后面任意数量的参数,并将它们存入 files 列表中。如果用 if-elif-else 实现相同逻辑,代码会更冗长且可读性较差。
更高级的用法:使用 |(或)和守卫条件(guard)
接下来,我们引入 或操作符 | 和 case 内部的 if 条件(称为“守卫”):
>>> def file_handler_v2(command):
... match command.split():
... case ['show']:
... print('List all files and directories: ')
... # 列出文件
... case ['remove' | 'delete', *files] if '--ask' in files:
... del_files = [f for f in files if len(f.split('.')) > 1]
... print('Please confirm: Removing files: {}'.format(del_files))
... # 获取用户确认后删除文件
... case ['remove' | 'delete', *files]:
... print('Removing files: {}'.format(files))
... # 直接删除文件
... case _:
... print('Command not recognized')
- 第二和第三个 case 都能匹配
'remove'或'delete'。 - 第二个 case 还包含一个守卫条件
if '--ask' in files,只有当输入中包含--ask标志时才会触发。 - 我们还用列表推导式过滤出带扩展名的文件(即
len(f.split('.')) > 1),这是一种简洁的 for 循环写法。 - 第三个 case 则在没有
--ask标志时匹配。
运行示例:
>>> file_handler_v2('remove --ask file1.txt file2.jpg file3.pdf')
Please confirm: Removing files: ['file1.txt', 'file2.jpg', 'file3.pdf']
>>> file_handler_v2('delete file1.txt file2.jpg file3.pdf')
Removing files: ['file1.txt', 'file2.jpg', 'file3.pdf']
关于 match-case 语句的最后思考
在本文中,我们介绍了 Python 的结构化模式匹配(match-case 语句),展示了它相比 if-elif-else 在代码量和可读性上的优势。
match-case 的能力远不止于此——你还可以传入对象(而非字符串),并对对象的属性进行模式匹配,非常强大!
重要提醒:对于像上面最后一个例子这样的复杂匹配,case 的顺序会直接影响程序行为。这与 if-elif-else 中的条件顺序类似。
举个例子:如果把第二和第三 case 的位置互换,--ask标志将永远不会被匹配到!因此,在编写 case 时务必仔细考虑顺序。
严格来说,match-case 并没有为 Python 增加全新的功能,但它极大地简化了复杂的控制逻辑。建议你将本文所学应用到自己的项目中,尝试将一些现有的 if-elif-else 语句重写为 match-case 形式,亲身体验它的优势。
祝你编码愉快!