整个九月份, 我都在忙一个 JS 的项目. 因为好多年不写 JS, 动手之前特地找了一些 JS 的资料恶补半天, 结果发现 JS 越来越像 Python 了. 且不说 JS 从基于原型的面向对象转向了基于类的面向对象, 单是类型化数组 (Typed Arrays) 的引入, 就让用惯了 NumPy 的我喜出望外. 另外, JS 的数组推导式和装饰器, 也几乎是完全照抄了 Python 的作业.
进入十月, Python 社区指导委员会正式推出了 Python3.10, 距离上一个版本发布, 正好过去了一年. 记得 Python3.9 发布之后没几天, 我写过一篇名为《危险的转变: Python 正在从简明转向臃肿, 从实用转向媚俗》的博文, 着实吐槽了一番, 正所谓爱之也深恨之也切. 这次新版本发布, 我自然不会放过尝鲜的机会, 赶紧安装尝试了一下, 却发现, 一向特立独行的 Python 居然开始抄作业了, 这次抄的是 Rust.
众所周知, Rust 有两样镇山之宝, 一是安全的内存模型, 二是模式匹配. 在内存管理上, Python 使用了传统的垃圾回收的内存模型, 和 Rust 没有多少可比性. 随着 Python3.10 的发布, 模式匹配被引入到 Python 中, 而且几乎是完全照搬了 Rust 的概念. Rust 支持模式匹配中的变量绑定, 结构体 / 元组解构, 守卫条件判断, 数值范围匹配等特性, Python 照单全收, 连下划线 _ 匹配任意情形也原封不动地继承了过来.
让我们一起来揭开 Pyhton3.10 最重要的升级 -- 模式匹配的盖头.
类似 C 语言的 switch case, Python 的模式匹配最简单的应用就是对字面值进行匹配:
- >>> a = 3
- >>> match(a):
- case 1:
- print("a == 1")
- case 2:
- print("a == 2")
- case _: # default
- print("other")
- other
case 语句中, 支持或操作:
- >>> import datetime
- >>> n = datetime.datetime.now()
- >>> match(n.weekday()):
- case 0|1|2|3|4: print("工作日")
- case 5|6: print("周末")
工作日
除了字面值外, case 语句, 支持对上面提到的模式进行解构, 如对元组:
- >>> a = (0, 1)
- >>> match(a):
- case (0, y): # 匹配所有第 0 个元素是 0 的元组
- print(f"a[0]==0, a[1]=={y}")
- case (x, 0): # 匹配所有第 1 个元素是 0 的元组
- print(f"a[1]==0, a[0]=={x}")
- a[0]==0, a[1]==1
对列表:
- >>> cmd = "ls test"
- >>> match(cmd.split()):
- case ["ls", path]: print(f"显示 {path} 中的文件和目录")
- case ["rm", path]: print(f"删除 {path} 中的文件和目录")
- case ["cp", src, dest]: print(f"将 {src} 复制到{dest}")
显示 test 中的文件和目录
对字典:
- >>> a = {"name": "xxx", "age": 40, "job": "程序员"}
- >>> match(a):
- case {"name": name, "age": age, "job": "程序员"}:
- print(f"他是一名程序员, 名字叫 {name}, {age} 岁了")
- case {"name": name, "age": age, "job": "教师"}:
- print(f"他是一名人民教师, 名字叫 {name}, {age} 岁了")
他是一名程序员, 名字叫 xxx, 40 岁了
对于类对象, match case 照样可以使用如:
- >>> class Point():
- def __init__(self,x,y):
- self.x = x
- self.y = y
- >>> a = Point(1, 2)
- >>> match(a):
- case Point(x=1, y=y): print(f"这是一个 X 坐标为 1 的点, 它的 Y 坐标为{y}")
- case Point(x=x, y=2): print(f"这是一个 Y 坐标为 2 的点, 它的 X 坐标为{x}")
这是一个 X 坐标为 1 的点, 它的 Y 坐标为 2
也可以用于多个类:
- >>> class Programmer:
- def __init__(self, lang):
- self.lang = lang
- >>> class Teacher:
- def __init__(self, subject):
- self.subject = subject
- >>> a = Programmer("Python")
- >>> match(a):
- case Programmer(lang="Python"): print("咱们都是 Pyhon 程序员!")
- case Programmer(): print("原来你也是一名程序员!")
- case Teacher(): print("向人民教师致敬!")
咱们都是 Pyhon 程序员!
case 语句后, 还支持添加一个 if 语句, 进一步对匹配的条件进行限制, 这个 if 语句, 被称之为 "守卫". 如:
- >>> class Point():
- def __init__(self,x,y):
- self.x = x
- self.y = y
- >>> a = Point(2,2)
- >>> match(a):
- case Point(x=x, y=y) if x==y: print("这个点在斜率为 1 的直线上")
- case Point(x=x, y=y) if x==-y: print("这个点在斜率为 - 1 的直线上")
这个点在斜率为 1 的直线上
美中不足的是, 我没有找到 case 语句中直接使用范围的方法, 但这个可以用守卫来解决:
- >>> a = 5
- >>> match(a):
- case x if 1 <= x < 10: print("数字在 1 和 10 之间")
- case x if 10 <= x < 20: print("数字在 10 和 20 之间")
数字在 1 和 10 之间
来源: http://developer.51cto.com/art/202110/687780.htm