Python 在 3.10 之前没有提供类似 switch case 一样的分支语句,虽然也能用 if elif else 或者字典来实现同样的逻辑。到 3.10 版本终于提供了 match case 结构化模式匹配,该语法比 switch case 更加灵活和强大。
match 语句接受一个表达式并将其值与以一个或多个 case 语句块形式给出的一系列模式进行比较。和其他语言 switch case 语句很不同的一点是,match case 不用 break 进行退出,每个 case 是独立的逻辑。
匹配字面值其他 case 条件都不满足时,可以用 _ (下划线)通配符进行捕获(同其它语言 default 分支)。可使用 | (或)组合多个 case 条件,满足其中一项时就匹配(同其它语言多个 case 只有一个break 的情况)。
def func(str):
match str:
case 'cpp':
print('cpp')
case 'java':
print('java')
case 'js' | 'python': #case不用break跳出,多个条件时用或连接
print('js or python')
case _: #通配符,类似c语系switch语句的default分支,其他case都不满足时进入
print('other')
func('python') #js or python
func('cpp') #cpp
func('sql') #other
带有字面值和变量的模式
模式可以看起来像解包形式,而且模式可以用来绑定变量。
#tuple变量
point=(1,2)
match point:
case (0,0):
print('1:0,0')
case (x,0):
print(f'2:{x},0')
case (0,y):
print(f'3:0,{y}')
case (x,y):
print(f'4:{x},{y}')
模式和类
如果使用类来结构化你的数据,可以使用类的名字,后面跟一个类似构造函数的参数列表,作为一种模式。这种模式可以将类的属性捕捉到变量中:
class Point:
x: int
y: int
point=Point()
point.x=1
point.y=2
match point:
case Point(x=0,y=0):
print('1:0,0')
case Point(x=x,y=0):
print(f'2:{x},0')
case Point(x=0,y=y):
print(f'3:0,{y}')
case Point(x=x,y=y):
print(f'4:{x},{y}')
嵌套模式
模式可以任意地嵌套。 如果我们的数据是一个 list,则它可以这样被匹配:
def match_list(arg):
match arg:
case []:
print("空list")
case [1]:
print("一个一的list")
case [x]:
print(f"一个值({x})的list")
case [x,y]:
print(f"两个值({x},{y})的list")
case _:
print("其他情况")
match_list(NULL) #其他情况
match_list([]) #空list
match_list([1]) #一个一的list
match_list([2]) #一个值(2)的list
match_list([1,2]) #两个值(1,2)的list
match_list([1,2,3]) #其他情况
复杂模式和通配符
除了单独一个 _ 通配符的 case ,也可以用在更复杂的模式中。
status=('warning', 'test', 110)
match status:
case ('warning', code, 40):
print("warning 40")
case ('warning', code, _):
print(f"error {code}")
约束项
可以向一个模式添加 if
子句,称为“约束项”。 如果约束项为假值,则 match
将继续尝试下一个 case 语句块。 请注意值的捕获发生在约束项被求值之前。
match point:
case Point(x, y) if x == y:
print("xy相等")
case Point(x, y):
print("xy不等")
子模式
子模式可使用 as
关键字来捕获,引用匹配到的值
def func(item):
match item:
case ('a'|'b'|'c') as x:
print(f'hello {x}')
func('a') #hello a
def func(item):
match item:
case [('a'|'b'|'c') as x,y]:
print(f'hello {x},{y}')
func(['c','d']) #hello c,d
其他特性
类似于解包赋值,元组和列表模式具有完全相同的含义,而且实际上能匹配任意序列。 从技术上说,目标必须为一个序列。 因而,一个重要的例外是模式不能匹配迭代器。 而且,为了避免一个常见的错误,序列模式不能匹配字符串。
序列模式支持通配符: [x, y, *rest] 和 (x, y, *rest) 的作用类似于解包赋值中的通配符。 在 * 之后的名称也可以为 _,因此 (x, y, *_) 可以匹配包含两个条目的序列而不必绑定其余的条目。
映射模式: {"bandwidth": b, "latency": l} 会从一个字典中捕获 "bandwidth" 和 "latency" 值。与序列模式不同,额外的键会被忽略。 也支持通配符 **rest。(但 **_ 是冗余的,因而不被允许。)
大多数字面值是按相等性比较的。 但是,单例对象 True, False 和 None 则是按标识号比较的。
命名常量也可以在模式中使用。 这些命名常量必须为带点号的名称以防止常量被解读为捕获变量:
from enum import Enum
class Color(Enum):
RED = 0
GREEN = 1
BLUE = 2
match color:
case Color.RED:
print("I see red!")
case Color.GREEN:
print("Grass is green")
case Color.BLUE:
print("I'm feeling the blues :(")
参考
文档:PEP 636 -- Structural Pattern Matching: Tutorial | Python.org
文档:Python 3.10 有什么新变化 — Python 3.10.2 文档