Python中match...case的用法

在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]: # 匹配,但未命中,因为已经命中了前面的case
    print("[2,3,4,5]")
  case default: # 匹配,但未命中,因为已经命中了前面的case
    print("default = " + str(type(default)) + str(default))

集合(set):目前似乎还不支持集合数据类型的匹配(不确定,如有错误之处,望留言指正)

类(Class):首先判断是否属于同一个class,然后判断calss各属性值是否相等

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

obj = 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=1
  case {"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 = x
        self.y = y

或者使用标准库的dataclasses.dataclass装饰器,它会提供__match_args__属性,如下

from dataclasses import dataclass

@dataclass
class Point2:
    x: int
    y: int

这样就可以使用捕捉模式,如下

class Point:
    __match_args__ = ('x', 'y')

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


from dataclasses import dataclass

@dataclass
class Point2:
    x: int
    y: int


obj = 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不大于10
    print("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 = 6
  case [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")

你可能感兴趣的:(python,match,case,switch)