Python语法糖之match-case


本博客主要参考为北京大学陈斌老师的下一站Python

image

概述

match-case是python3.10+的新特性,可以理解为python中的switch-case。如果你想要使用它,请注明所需python>=3.10.

基本语法和语义

match <表达式>:
    case <值1>:
        <语句块1>
    case <值2> | <值3> | <值4> :
        <语句块2>
    case _:
        <语句块3>
  • 语义:计算表达式的值,依次匹配case后的值,一旦匹配到,就执行对应的语句块1次,语句结束;
  • 如果所有case都匹配不上的就执行case _:对应的语句块,语句结束。
  • case后必须跟“字面值”,也就是说,不能是表达式。

example1

# match-case的基本例子
color = input("请输入需要查询的颜色:")
match color:
    case "red" | "红" | "红色":
        r, g, b = 255, 0, 0
    case "green" | "绿" | "绿色":
        r, g, b = 0, 255, 0
    case "yellow" | "黄" | "黄色":
        r, g, b = 255, 255, 0
    case _:
        r, g, b = -1, -1, -1
if r >= 0:
    print(f"{color}的颜色代码:#{r:02X}{g:02X}{b:02X}")
else:
    print(f"查询不到{color}的颜色代码!")

example2

# 今天是星期几
from datetime import datetime
match datetime.today().weekday():
    case 0:
        print("今天是星期一")
    case 3:
        print("疯狂星期四!")
    case 4:
        print("加油,周末就到了!")
    case 5 | 6:
        print("周末愉快!")
    case _:
        print("普普通通的工作日")

进阶用法

如果在case写变量名

  • 不是你想象的那样匹配变量的值
  • case <名字>的含义是“捕捉”匹配不到的值
p = eval(input("请输入坐标(x,y):"))
match p:
    case (0, 0):
        print(f"{p}是原点")
    case (0, y):
        print(f"{p}在Y轴上,距离原点{abs(y)}")
    case (x, 0):
        print(f"{p}在X轴上,距离原点{abs(x)}")
    case (x, y):
        print(f"{p}距离原点{abs(x+y*1j)}")

只是为了不写if语句么?

  • 引入了match-case语句,我们得到一个C语言里switch语句的等价物
  • 可是只是为了把elif语句改一种形式?
  • 甚至丢掉了elif判断条件的灵活性
from datetime import datetime
if (w := datetime.today().weekday()) == 0:
    print("今天是星期一")
elif w == 3:
    print("疯狂星期四!")
elif w == 4:
    print("加油,周末就到了!")
elif w in (5, 6):
    print("周末愉快!")
else:
    print("普普通通的工作日")

匹配列表

match内涵比switch深

  • 不光是对于字面值判断相等
  • 对于序列,可以进行“模式匹配”
    • 空序列( []),任意长度的序列([ *_ ]);
    • 单个占位符(_)或者捕捉变量(a);
    • 多个占位符(*_)或者捕捉变量 (*a);

列表的模式匹配

alist = [int(x) for x in input().split()]
print("输入了:", alist)
match alist:
    case [ ]:
        print("空列表")
    case [1, _, third]:
        print("这是1开头的三个数,第三个数是:", third)
    case [1, *_, last]:
        print("这是1开头的列表,最后一个数是:", last)
    case [*_, 2]:
        print("这是以2结尾的列表!")
    case [_, 0, *remains]:
        print("这是第二个数为0的列表,0后面是:", remains)
    case [*_]:
        print("都没匹配上的随便列表。")

示例:摇骰子的统计

# 连续掷三次骰子,分别统计连续三次6、连续两次6、只有一次6的频率
from random import randint
n, triple, double, single = 1000000, 0, 0, 0
for i in range(n):
    alist = [randint(1, 6) for k in range(3)]
    match alist:
        case [6, 6, 6]:
            triple += 1
        case [6, 6, _] | [_, 6, 6]:
            double += 1
        case [6, _, _] | [_, 6, _] | [_, _, 6]:
            single += 1
print(f"模拟投掷{n:,}次,结果如下:")
print(f"- 连续三次6有{triple:8,d}次,频率为{triple/n*100:4.1f}%")
print(f"- 连续两次6有{double:8,d}次,频率为{double/n*100:4.1f}%")
print(f"- 仅有一次6有{single:8,d}次,频率为{single/n*100:4.1f}%")

匹配字典

字典的模式匹配

  • 包含case中列明的key:value模式即可,但key必须是字面值,没有占位符;
  • 匹配任何字典({ });
  • 单个value的占位符(_),或者捕捉变量(a);
  • 多个key:value的捕捉变量(**d),得到一个字典d
aset = {"Tom":20, "Jerry":19, "Spike":30, "Tyke":5}
match aset:
    #case { }:
    #    print("匹配到任意字典。")
    case { "Tom":201 }:
        print("包含了Tom:20")
    case { "Tom1":_ }:
        print("包含了Tom,value任意。")
    case { "Tom1":v }:
        print("包含了Tom,捕捉到value=", v)
    case { "Tom1":t, "Jerry1":j }:
        print("包含Tom和Jerry,对应的value分别是:", t, j)
    case { "Tyke1":_, **d }:
        print("包含Tyke,其余的key:value是:", d)
    case { **d }:
        print("匹配到任意字典,并且捕捉到d中:", d)

示例:分组执行任务的表现

# 模拟从工作人员名单中随机抽取派出执行任务
from random import randint, sample
workers = ["Tom", "Jerry", "Spike", "Tyke", "Smith", "Bob"]
n, tom_team, bob_grade, jerry_team, js_grade = 100, 0, 0, 0, 0
for i in range(n):
    team = {w: randint(40, 100) for w in sample(workers, 3)}
    match team:
        case {"Tom": _, "Bob": g}:
            tom_team += 1
            bob_grade += g
        case {"Jerry": g1, "Spike": g2}:  # 与上面case互斥
            jerry_team += 1
            js_grade += (g1 + g2)
print(f"一共派出了{n}次小组,")
print(f"Tom和Bob搭档的次数为{tom_team}")
print(f"--有Tom参与的情况下,Bob的平均业绩为{bob_grade/tom_team:.1f}")
print(f"Jerry和Spike搭档的次数为{jerry_team}")
print(f"--两人搭档的平均业绩为{js_grade/jerry_team/2:.1f}")
posted @ 2022-11-11 22:37  Cisco_coco  阅读(2133)  评论(0编辑  收藏  举报