在C语言中有switch...case语句,Pthon3.10之前应该是没有类似语法,从Python3.10开始引入match...case与switch分支语句用法类似,但有细微差别,总结如下:
1.语法
肉眼可见的是关键词从switch变成了match,同时match...case语句遵循Python语法风格,如下:
switch(condition)
{case 1:do_something_1();break;case 2:do_something_2();break;case 3:do_something_3();break;default:do_default();
}
以上是C语言中switch...case的语法,需要注意:case后面需要有break,否则会继续执行下面的语句
match condition:case 1:do_something_1()case 2:do_something_2()case 3:do_something_3()case _:do_default()
以上是Python中match...case的语法,没有break,也没有default,取而代之的是捕捉模式(Capture),比如本例中的case _: ,与default功能类似,表示其他值,同时_相当于一个局部变量,其值等于condition,_也可以取其他变量名,当然,一个match语句中最多只能有一个捕捉模式的case,且必须位于最后一个case,就像只能有一个default一样
2.数据类型支持
C语言中的case后面只能是整形值,Python的case后面可以是大部分数据类型,对于不同数据类型的匹配规则,列举如下:
数值(int,float) & 字符串(string) & None:使用等号规则进行匹配
n = -1.2
match n:case 0: # 不匹配print("1")case 1: # 不匹配print("1")case 1.1: # 不匹配print("1.1")case 1.2: # 不匹配print("1.2")case "-1.2": # 不匹配print("str -1.2")case -1.2: # 匹配,且命中print("-1.2")case default: # 匹配,但未命中,因为前面case已经命中print("default = " + str(default))
s = '3'
print("s == num(3) ? " + str(s == 3))
match s:case 1: # 不匹配print("1")case 3: # 不匹配print("3")case "3": # 匹配,且命中print("str 3")case default: # 匹配,但未命中,因为前面case已经命中print("default" + default)
n = None
match n:case 0: # 不匹配print("0")case "": # 不匹配print('""')case False: # 不匹配print("False")case None: # 匹配,且命中print("None")case default: # 匹配,当未命中,因为前面case已经命中print("default" + str(default))
布尔值(Boolean):比较特殊,True会匹配True和数字1,False会匹配False和数字0
b = True
match b:case "": # 不匹配print('""')case 0: # 不匹配print("0")case 11: # 不匹配print(11)case 1: # 匹配,且命中print(1)case True: # 匹配,但未命中,因为前面case已经命中print(True)case False: # 不匹配print(False)case default: # 匹配,但未命中,因为前面case已经命中print("default = " + str(default))
b = False
match b:case "": # 不匹配print('""')case 0: # 匹配,且命中print("0")case 11: # 不匹配print(11)case 1: # 不匹配print(1)case True: # 不匹配print(True)case False: # 匹配,但未命中,因为前面case已经命中print(False)case default: # 匹配,但未命中,因为前面case已经命中print("default = " + str(default))
字典(dictionary):当condition是个字典时,case后面只要是字典,且case后面字典的键值对在condition中都能找到,则该case命中,键值对无顺序要求,有一个比较特殊情况,假如case后面跟的是空字典,那么不管condition字典内容是什么,该case必然命中
a = {"ab":3, "5":5}
match a:case 1: # 不匹配print("1")case {"ab":2}: # 不匹配print("ab2")case {"ab":1}: # 不匹配print("ab1")case {"c":3}: # 不匹配print("c3")case {"5":5, "ab":3}: # 匹配,且命中print("5:5 ab:3")case {"ab":3, "t":3}: # 不匹配print("ab3 t3")case {"ab":3, "5":4}: # 不匹配print("ab3 5:4")case {"ab":3, "5":5, "t":2}: # 不匹配print("ab3 5:5 t2")case {"ab":3, "5":5}: # 匹配,但未命中,因为前面case已经命中print("ab3 5:5")case {"ab":3}: # 匹配,但未命中,因为前面case已经命中print("ab3")case {}: # 匹配,但未命中,因为前面case已经命中print("{}")case _b: # 匹配,但未命中,因为前面case已经命中print("_______" + str(_b))
列表 (list)& 元组(tuple):当condition是个列表或元组时,在做case比对时不分列表和元组,只要元素数量相同,且每个索引位置值相同,即可匹配成功
l = [2,3,4,5]
match l:case 2: # 未匹配print("2")case 2,3: # 未匹配print("2,3")case 2,3,4,5,6: # 未匹配print("2,3,4,5,6")case [2,4,3,5]: # 未匹配print("[2,4,3,5]")case (2,4,3,5): # 未匹配print("(2,4,3,5)")case (2,3,4,5): # 匹配,且命中print("(2,3,4,5)")case [2,3,4,5]: # 匹配,但未命中,因为已经命中了前面的caseprint("[2,3,4,5]")case default: # 匹配,但未命中,因为已经命中了前面的caseprint("default = " + str(type(default)) + str(default))
集合(set):目前似乎还不支持集合数据类型的匹配(不确定,如有错误之处,望留言指正)
类(Class):首先判断是否属于同一个class,然后判断calss各属性值是否相等
class Point:def __init__(self, x, y):self.x = xself.y = y
class Point2:def __init__(self, x, y):self.x = xself.y = yobj = Point(1,2)
match obj:case Point(x=3, y=4): # 不匹配,属性值不相等print("Point,3,4")case Point2(x=1, y=2): # 不匹配,class类型不一致print("Point2,1,2")case {"x":1, "y":2}: # 不匹配,Class类型不一致print("{x,y}")case Point(x=1, y=2): # 匹配,且命中print("Point,1,2")case default:print("default=" + str(default))
3.组合case
在C语言中,可以多个case对应一个执行过程,如下所示
switch(condition)
{case 1:do_something_1();break;case 2:case 3:do_something_2_3();break;case 4:case 5:do_something_4_5();break;default:do_default();
}
当condition等于2或3时,执行do_sometion_2_3()函数,当condition等于4或5时,执行do_something_4_5()函数
在Python中也有类似写法,叫作组合(OR)模式,如下所示
c = 5
match c:case 1: # 不匹配print("1")case 1 | 2: # 不匹配print("1 | 2")case 3 | 4: # 不匹配print("3 | 4")case 5 | 6 | 7: # 匹配,且命中print("5 | 6 | 7")case 5: # 匹配,但未命中,因为前面case已经命中print("5")case _: # 匹配,但未命中,因为前面case已经命中print("default = " + str(_))
4.通配符,捕捉模式的扩展
当捕捉模式应用到列表和字典等复杂数据类型时,情况会比较复杂,我们通过几个例子来进行说明
d = [1,2,3,4]
match d:case a,b: # 不匹配,元素数量不等print("a,b")print([a,b])case a,b,c: # 不匹配,元素数量不等print("a,b,c")print([a,b,c,d])case 2,*b: # 不匹配,索引位值不相等,其中*b代表任意个元素print("2,*b")print(b)case 1,*b,3: # 不匹配,索引位值不相等print("1,*b,3")print(b)case *b,2: # 不匹配,索引位值不相等print("*b,2")print(b)case 1,2,3,*b,4: # 匹配,且命中print("1,2,3,*b,4")print(b) // b = []case 1,*b,4: # 匹配,但未命中,因为前面case已命中print("1,*b,4")print(b)case *b,4: # 匹配,但未命中,因为前面case已命中print("*b,4")print(b)case 1,*b: # 匹配,但未命中,因为前面case已命中print("1,*b")print(b)case *b,: # 匹配,但未命中,因为前面case已命中print("*b,")print(b)case [*b]: # 匹配,但未命中,因为前面case已命中print("[*b]")print(b)case 2,b,c,d: # 不匹配,索引位值不相等print("2,b,c,d")print([2,b,c,d])case 1,2,c,d: # 匹配,但未命中,因为前面case已命中print("1,2,c,d")print([1,2,c,d])case 1,b,c,d: # 匹配,但未命中,因为前面case已命中print("1,b,c,d")print([1,b,c,d])case a,b,c,d: # 匹配,但未命中,因为前面case已命中print("a,b,c,d")print([a,b,c,d])case _: # 匹配,但未命中,因为前面case已命中print("_")
本例中的a,b,c,d为捕捉模式在列表中的应用,而*b为通配符,表示匹配任意个元素,包括0个元素,且一个case中只能有一个通配符变量
类似地,捕捉模式和通配符还可以应用到字典数据类型,如下
d = {"a":1, "b":"bb", "c": [3,4], "d": {"dd":5}}
match d:case {"s": 1}: # 不匹配print("s:1")case {"a":a}: # 匹配,且命中print("a:a")print(a) # a=1case {"b":bbb}: # 匹配,但未命中,因为前面case已经命中print("b:bbb")print(bbb) # bbb = "bb"case {"a":1, **p}: # 匹配,但未命中,因为前面case已经命中print("a:1:**p")print(p) # p = {"b":"bb", "c": [3,4], "d": {"dd":5}}case {**p}: # 匹配,但未命中,因为前面case已经命中print("**p")print(p) # p = {"a":1, "b":"bb", "c": [3,4], "d": {"dd":5}}case {"d":d, **p}: # 匹配,但未命中,因为前面case已经命中print("d:d:**p")print(p) # p = {"a":1, "b":"bb", "c": [3,4]}case _: # 匹配,但未命中,因为前面case已经命中print("default=" + str(default)) # default = d = {"a":1, "b":"bb", "c": [3,4], "d": {"dd":5}}
倘若要将捕捉模式和通配符用于自定义类,需要给自定义类定义一个__match_args__数组,如下
class Point:__match_args__ = ('x', 'y')def __init__(self, x, y):self.x = xself.y = y
或者使用标准库的dataclasses.dataclass装饰器,它会提供__match_args__
属性,如下
from dataclasses import dataclass@dataclass
class Point2:x: inty: int
这样就可以使用捕捉模式,如下
class Point:__match_args__ = ('x', 'y')def __init__(self, x, y):self.x = xself.y = yfrom dataclasses import dataclass@dataclass
class Point2:x: inty: intobj = Point2(1,2)
match obj:case Point(x, y): # 不匹配,Class类型不一致print(f'Point({x=},{y=})')case Point2(x, y): # 匹配,且命中print(f'Point2({x=},{y=})')case Point(x=3, y=4): # 不匹配,Class类型不一致print("Point,3,4")case Point2(x=1, y=2): # 匹配,但未命中,因为前面case已经命中print("Point2,1,2")case {"x":1, "y":2}: # 不匹配,Class类型不一致print("{x,y}")case Point(x=1, y=2): # 不匹配,Class类型不一致print("Point,1,2")case default:# 匹配,但未命中,因为前面case已经命中print("default=" + str(default))
5.数据类型匹配
简单来说就是只根据数据类型来判断是否匹配,如下
a = {1,2,3}
match a:# case object():# print("object")case int(): # 不匹配print("int")case str(): # 不匹配print("str")case list(): # 不匹配print("list")case tuple(): # 不匹配print("tuple")case dict(): # 不匹配print("dict")case set(): # 匹配,命中print("set")case bool(): # 不匹配print("bool")case default:print("default=" + str(type(default)) + str(default))
需要注意的是True和False会匹配int()和bool(),所有值都会匹配object
6.AS语法
简单来说就是当匹配某个case时,将匹配到的值存入用as语句声明的新变量中,通常和数据类型匹配相结合使用,如下
a = [1,2,3,"4"]
match a:case {"b": bb} as v: # 不匹配print("b:bb:v")print(bb)print(v)case [1, *b, int() as v]: # 不匹配print("[1, *b]:int,v")print(b)print(v)case [1, *b, str() as v]: # 匹配,命中print("[1, *b]:str,v")print(b) # b = [2,3]print(v) # v = "4"case _:print("default = " + str(type(default)) + str(default))
7.补充条件模式
可以在case后面使用if语句来添加匹配的判断条件,如下
a = [3, 4, 5, 6]
match a:case [a, *b, c] if a>10: # 不匹配,a不大于10print("a>10")print("c=" + str(c))case [a, *b, c] if a>5: # 不匹配print("10>a>5")print("c=" + str(c))case [a, *b, c] if a>0: # 匹配print("5>a>0")print("c=" + str(c)) # c = 6case [a, *b, c] if a<0: # 不匹配print("a<0")print("c=" + str(c))case _:print("a=0")
再比如
cmd = ["go", "away"]
match cmd:case ["go", dir] if dir in ["east", "west"]: # 不匹配print("go->" + dir)case ["go", dir] if dir == "north" or dir == "south": # 不匹配print("stop")case ["go", dir]: # 匹配,命中print("unknown dir:" + dir)case _:print("default")