【Python】match 语句

news/2024/12/22 11:24:52/

该特性已经有 final 版本 since Python 3.10,出自 PEP 636,因此本文就该版本完整介绍 match 语句的各种花里胡哨的用法。

match 语句,或者说是 match-case 语句更为适合,和其他语言的 switch-case 语句类似,用作多条件的分支选择。在 Python 中,case 关键词的后面叫做模式(pattern)。

匹配字面值

这是最基本的用法,和:

def http_error(status):match status:case 400:return "Bad request"case 404:return "Not found"case 418:return "I'm a teapot"case _:return "Something's wrong with the internet"

这是一个根据状态码返回响应消息的函数,不同值对应不同的结果,特别地,_ 是通配符,表示一定会匹配成功,必须在最后一个 case 分支,充当类似其他语言 default 分支的功能,但是位置没有 default 自由。

大多数字面值是按相等性比较的,但是单例对象 True, FalseNone 则是按标识号比较的。

使用 | 在一个模式中还可以组合多个字面值:

case 401 | 403 | 404:return "Not allowed"

| 不止用于值的组合,后续介绍的更为复杂模式都可以通过该符号表“或”关系,详见后面示例。

匹配常量

模式可以使用命名常量。 这些命名常量必须为带点号的名称以防止它们被解读为捕获变量,说白了就是枚举
这个没什么说的,直接上实例,后续结合其他模式再给出复杂示例。

from enum import Enumclass Color(Enum):RED = 'red'GREEN = 'green'BLUE = 'blue'color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))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 :(")

匹配序列

常见的序列如列表、集合、元组等,这里以列表为例,给出一个比较综合的示例:

def match_sequence(seq):match seq:case []:print("序列为空")case [a]:print(f"序列只有一个元素:{a}")case [x, y]:print(f"序列有两个元素,[{x}, {y}]")case [b, 1]:print(f"序列有两个元素,第一个元素为 {b}")case [_, _, c]:print(f"序列有三个元素,第三个元素为 {c}")case _:print(f"序列未知")match_sequence([])                # 序列为空
match_sequence([2])               # 序列只有一个元素:2
match_sequence([4, 1])            # 序列有两个元素,[4, 1]
match_sequence([1, 2, 3])         # 序列有三个元素,第三个元素为 3
match_sequence([1, 2, 3, 4, 5])   # 序列未知
  • 匹配序列时,会按照元素顺序逐一比较
  • 模式中可以使用变量进行解构赋值
  • 会按照从上到下的顺序匹配模式,匹配成功就执行子句返回结果
  • 序列中可以使用 _ 职位占位符,表示那里有元素,但不关注值,注意与模式 _ 含义区分
  • 不能匹配迭代器和字符串

扩展解构

序列模式支持扩展解构操作:[x, y, *rest](x, y, *rest) 的作用类似于解包赋值。 在 * 之后的名称也可以为 _,因此,(x, y, *_) 可以匹配包含至少两个条目的序列,而不必绑定其余的条目。

def match_sequence2(seq):match seq:case [1, *p]:print(p)case [3, a, *_]:print(f"a={a}")case [_, _, *q]:print(q)match_sequence2([1, 2, 3, 4])    # [2, 3, 4]
match_sequence2([3, 4, 5, 6])    # a=4
match_sequence2([2, 3, 4, 5])    # [4, 5]

匹配字典

有了前面的基础,匹配字典值相对容易理解:

def match_dict(d):match d:case {"name": name, "age": age}:print(f"name={name},age={age}")case {"key": _, "value": value}:print(f"value={value}")case {"first": _, **rest}:print(rest)case _:passd1 = {"name": "ice", "age": 18}
d2 = {"key": "k", "value": "v"}
d3 = {"first": "one", "second": "two", "third": "three"}match_dict(d1)    # name=ice,age=18
match_dict()      # value=v
match_dict(d3)    # {'second': 'two', 'third': 'three'}

**rest 等解构操作也支持,但 **_ 是冗余的,不允许使用。

匹配对象

通过类对象可以结构化你的数据,通过使用类名字后面跟一个类似构造函数的参数列表,这种模式可以将类的属性捕捉到变量中:

class Point:x: inty: intdef location(point):match point:case Point(x=0, y=0):print("坐标原点")case Point(x=0, y=y):print(f"Y={y}")case Point(x=x, y=0):print(f"X={x}")case Point(x=m, y=n):print(f"X={m}, Y={n}")case Point():print("这个点不在轴上")case _:raise ValueError("未法的坐标数据")p1 = Point()
p2 = Point()
p2.x = 0
p2.y = 4
p3 = Point()
p3.x = 5
p3.y = 6location(p1)  # 这个点不在轴上
location(p2)  # Y=4
location(p3)  # X=5, Y=6
  • 这里特地将属性定义到类中而不是 __init__ 方法中,就是为了区分这种模式和构造函数的区别
  • 不写参数说明只关注是不是 Point 对象,其属性值无所谓

下面再看个有初始化参数的例子:

class Direction:def __init__(self, horizontal=None, vertical=None):self.horizontal = horizontalself.vertical = verticaldef direction(loc):match loc:case Direction(horizontal='east', vertical='north'):print('You towards northeast')case Direction(horizontal='east', vertical='south'):print('You towards southeast')case Direction(horizontal='west', vertical='north'):print('You towards northwest')case Direction(horizontal='west', vertical='south'):print('You towards southwest')case Direction(horizontal=None):print(f'You towards {loc.vertical}')case Direction(vertical=None):print(f'You towards {loc.horizontal}')case _:print('Invalid Direction')d1 = Direction('east', 'south')
d2 = Direction(vertical='north')
d3 = Direction('centre', 'centre')# 应用
direction(d1)  # You towards southeast
direction(d2)  # You towards north
direction(d3)  # Invalid Direction

匹配位置属性

你可以在某些为其属性提供了排序的内置类(例如 dataclass)中使用位置参数。

from dataclasses import dataclass@dataclass
class Point:x: inty: int

你也可以通过在你的类中设置 __match_args__ 特殊属性来为模式中的属性定义一个专门的位置。

class Point:__match_args__ = ("x", "y")x: inty: int

以下模式是等价的:

Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)

都是将 y 属性绑定到 var 变量。

匹配内建类

相当于在解构赋值时加了一层校验,只有符合类型要求的才会匹配成功。

def match_type(arg):match arg:case [int(), int(a), str()]:print(a)case list(l):print(l)case {"one": str(b)}:print(b)match_type([1, 2, "3"])    # 2
match_type([1, 2, 3])      # [1, 2, 3]
match_type({"one": "1"})   # 1

嵌套模式

模式可以任意地嵌套。 例如,如果我们的数据是由点组成的短列表(类似 [Point(x1, y1), Point(x2, y2)] 形式),则它可以这样被匹配:

match points:case []:print("列表中没有点")case [Point(0, 0)]:print("原点是列表中唯一的点")case [Point(x, y)]:print(f"列表中有一个点{x}{y}")case [Point(0, y1), Point(0, y2)]:print(f"Y轴上 {y1}{y2} 处的两点在列表中")case _:print("列表中还有其他内容")

或者:

def match_multiple(arg):match arg:case ["age", *l]:print(l)case ["language", "C++" | "Java" | "Python", *t]:print(t)match_multiple(["age", 18, 29, 30])  # [18, 29, 30]
match_multiple(["language", "Java", 2, 3, 4, 5, 6])  # [2, 3, 4, 5, 6]
match_multiple(["language", "Python", 7, 8, 9, 10, "J"])  # [2, 3, 4, 5, 6]

模式捕获

模式捕获是为了在模式匹配成功后,获得该模式或者其子模式的值使用,一般用法是 模式 as 变量名

match arg:case "早" | "中" | "晚" as time:print(time)case "一" | "二" | "三" as number:print(number)case [(a, 2) as t, 3]:print(t)

条件匹配

为模式添加成为守护项的 if 子句。如果守护项的值为假,则 match 继续匹配下一个 case 语句块。注意,值的捕获发生在守护项被求值之前

match point:case Point(x, y) if x == y:print(f"Y=X at {x}")case Point(x, y):print(f"Not on the diagonal")

http://www.ppmy.cn/news/611879.html

相关文章

background 与 background-image

background background 简写属性在一个声明中设置所有的背景属性。 可以设置如下属性: background-colorbackground-positionbackground-sizebackground-repeatbackground-originbackground-clipbackground-attachmentbackground-image 如果不设置其中的某个值&a…

快手推荐系统及 Redis 升级存储

快手推荐系统及 Redis 升级存储 借傲腾™ 补上 DRAM 短板 内容简介: 作为短视频领域的领先企业,快手需要不断导入更先进的技术手段来调整和优化其系统架构,以应对用户量和短视频作品数量的爆炸式增长; 这其中,作…

tar 和gzip 的区别

首先要 弄清两个概念:打包和压缩。 打包是指将一大堆文件或目录什么的变成一个总的文件, 压缩则是将一个大的文件通过一些压缩算法变成一个小文件。 为什么要区分这两个概念呢?其实这源于Linux中的很多压缩程序只能针对一个文件进行压缩&…

OpenCV+python:人脸检测

1,人脸检测简介 人脸检测的模型主要有两类,一类是知识模型,根据眼睛、嘴、鼻子的相对位置或面部不同部位的颜色深度差异来检测人脸,另一类是统计模型,把海量的人脸数据转换成二维像素矩阵,从统计的观点出发…

英特尔内存革新助平安云 Redis 云服务降本增效

英特尔内存革新助平安云 Redis 云服务降本增效 英特尔 傲腾™ 数据中心级持久内存的引入,为平安云的降本增效开启了一条新的道路。通过对平安云 Redis 数据库产品的支持,用户能享受到性能优异且价格实惠的云服务,接下来我们还将通过更多类似…

JAVA 包装类

JAVA 包装类 1 包装类 把基本类型进行包装,提供更加完善的功能。 基本类型是没有任何功能的,只是一个变量,记录值,而包装类可以有更加丰富的功能 1.1 与基本类型的对应关系 1.2 Number 数字包装类的抽象父类。 提供了各种获取值的方式。 1.3 Integer 创建对象…

Asp.Net Core在线生成二维码

前言: 原先用zxing Code写过基于Winfrom的批量生成二维码工具,以及单个生成二维码工具;批量生成二维码Gihub源代码 今天尝试用QRCoder 加 Asp.Net Core 写了一个在线生成二维码的例子,并且保存图片到Ubuntu系统; 代码&…

TensorFlow平台搭建

1,Anaconda 的安装 Anaconda指的是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项,Conda是一个开源的包、环境管理器,可以用于在同一个机器上安装不同版本的软件包及其依赖,并能够在不同的…