python:初识v3.10结构化模式匹配
python3.10
增加了采用模式加上相应动作的 match 语句 和 case 语句 的形式的结构化模式匹配。 模式由序列、映射、基本数据类型以及类实例构成。 模式匹配使得程序能够从复杂的数据类型中提取信息、根据数据结构实现分支,并基于不同的数据形式应用特定的动作。
语法与操作
•模式匹配的通用语法如下:
match subject:
case <pattern_1>:
<action_1>
case <pattern_2>:
<action_2>
case <pattern_3>:
<action_3>
case _:
<action_wildcard>
match
语句接受一个表达式并将其值与以一个或多个 case 语句块形式给出的一系列模式进行比较。 具体来说,模式匹配的操作如下:
•使用具有特定类型和形状的数据 (subject)•针对 subject 在 match 语句中求值•从上到下对 subject 与 case 语句中的每个模式进行比较直到确认匹配到一个模式。•执行与被确认匹配的模式相关联的动作。•如果没有确认到一个完全的匹配,则如果提供了使用通配符 _ 的最后一个 case 语句,则它将被用作已匹配模式。 如果没有确认到一个完全的匹配并且不存在使用通配符的 case 语句,则整个 match 代码块不执行任何操作。
简单模式:匹配一个字面值
让我们把这个例子看作是模式匹配的最简单形式:一个值,即主词,被匹配到几个字面值,即模式。在下面的例子中,status 是匹配语句的主词。模式是每个 case 语句,字面值代表请求状态代码。匹配后,将执行与该 case 相关的动作:
def http_error(status):
match status:
case 400:
return "请求错误!"
case 404:
return "页面不存在!"
case 418:
return "yyds!"
case _:
return "网络有点问题!"
if __name__ == '__main__':
print(http_error(500))
运行结果
网络有点问题!
Process finished with exit code 0
如果传给上述函数的 status
为 418
,则会返回 "yyds!"
。 如果传给上述函数的 status
为 500
,则带有_
的 case
语句将作为通配符匹配,并会返回 "网络有点问题!"。
请注意最后一个代码块:变量名_
将作为 通配符 并确保目标将总是被匹配。_
的使用是可选的。
•你可以使用 | (“ or ”)
在一个模式中组合几个字面值:
def http_error(status):
match status:
case 400 | 404 | 403:
return "请求错误!"
case _:
return "网络有点问题!"
if name == 'main': print(http_error(500))
运行结果
```python
网络有点问题!
Process finished with exit code 0
•无通配符的行为
如果我们修改上面的例子,去掉最后一个case
块,这个例子就变成:
def http_error(status):
match status:
case 400 | 404 | 403:
return "请求错误!"
if __name__ == '__main__':
print(http_error(200))
运行结果
None
Process finished with exit code 0
如果不在case
语句中使用 _
,可能会出现不存在匹配的情况。如果不存在匹配,则行为是一个无操作行为。例如,如果传入了值为200
的 status
,就会直接返回函数的默认值None
。
带有字面值和变量的模式
模式可以看起来像解包形式,而且模式可以用来绑定变量。 在这个例子中,一个数据点可以被解包为它的x
坐标和y
坐标:
# point 是一个 (x, y) 元组
match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("Not a point")
第一个模式有两个字面值(0, 0)
,可以看作是上面所示字面值模式的扩展。接下来的两个模式结合了一个字面值和一个变量,而变量 绑定 了一个来自主词的值(point)
。 第四种模式捕获了两个值,这使得它在概念上类似于解包赋值(x, y) = point
。
•一个简单的例子:
point = (60, 60)
match point:
case (0, 0):
print("坐标原点")
case (0, y):
print(f"Y={y}")
case _:
raise ValueError("Not a point")
运行结果
Traceback (most recent call last):
File "E:\git_dfwsgroup\9first\xzProjectTeamApi\lms3_9first_com\apiInterface\web\classify.py", line 16, in <module>
raise ValueError("Not a point")
ValueError: Not a point
Process finished with exit code 1
模式和类
如果你使用类来结构化你的数据,你可以使用类的名字,后面跟一个类似构造函数的参数列表,作为一种模式。这种模式可以将类的属性捕捉到变量中:
class Point:
x: int
y: int
def location(point):
match point:
case Point(x=0, y=0):
print("Origin is the point's location.")
case Point(x=0, y=y):
print(f"Y={y} and the point is on the y-axis.")
case Point(x=x, y=0):
print(f"X={x} and the point is on the x-axis.")
case Point():
print("The point is located somewhere else on the plane.")
case _:
print("Not a point")
•一个简单的例子:
class Point:
x: int
y: int
def location(point):
match point:
case Point(x=0, y=0):
print("原点是点的位置。")
case Point(x=0, y=y):
print(f"Y={y} 这个点在y轴上。")
case Point(x=x, y=0):
print(f"X={x} 这个点在x轴上。")
case Point():
print("这个点位于平面上的其他地方。")
case _:
print("不是一个点")
p = Point()
p.x = 0
p.y = 0
location((p.x, p.y))
运行结果
不是一个点
Process finished with exit code 0
•上述代码中就是把类中的属性值进行判断,当然,如果使用构造方法就会减少对象调用属性的操作:
class Point:
def __init__(self, x, y):
self.x: int = x
self.y: int = y
def location(point):
match point:
case Point(x=0, y=0):
print("原点是点的位置。")
case Point(x=0, y=y):
print(f"Y={y} 这个点在y轴上。")
case Point(x=x, y=0):
print(f"X={x} 这个点在x轴上。")
case Point():
print("这个点位于平面上的其他地方。")
case _:
print("不是一个点")
location(Point(0, 1))
运行结果
Y=1 这个点在y轴上。
Process finished with exit code 0
•您也可以使用官方提供的装饰器操作:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def location(point):
match point:
case Point(x=0, y=0):
print("原点是点的位置。")
case Point(x=0, y=y):
print(f"Y={y} 这个点在y轴上。")
case Point(x=x, y=0):
print(f"X={x} 这个点在x轴上。")
case Point():
print("这个点位于平面上的其他地方。")
case _:
print("不是一个点")
location(Point(0, 1))
运行结果
Y=1 这个点在y轴上。
Process finished with exit code 0
这个装饰器的作用,就是把没有构造方法(__init__
)的类,添加了一个构造方法。
嵌套模式
模式可以任意地嵌套。 例如,如果我们的数据是由点组成的短列表,则它可以这样被匹配:
match points:
case []:
print("No points in the list.")
case [Point(0, 0)]:
print("The origin is the only point in the list.")
case [Point(x, y)]:
print(f"A single point {x}, {y} is in the list.")
case [Point(0, y1), Point(0, y2)]:
print(f"Two points on the Y axis at {y1}, {y2} are in the list.")
case _:
print("Something else is found in the list.")
•一个简单的例子:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def location(point):
match point:
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("在列表中找到了其他东西。")
location([Point(0, 2), Point(0, 1)])
运行结果
Y轴上2、1处的两点在列表中。
Process finished with exit code 0
复杂模式和通配符
到目前为止,这些例子仅在最后一个case
语句中使用了 _
。 但通配符可以被用在更复杂的模式中,例如 ('error', code, _)
。 举例来说:
match test_variable:
case ('warning', code, 40):
print("A warning has been received.")
case ('error', code, _):
print(f"An error {code} occurred.")
•一个简单的例子:
def location(point):
match point:
case ('warning', code, 500):
print(f"收到了一个警告 {code}。")
case ('error', code, _):
print(f"发生错误 {code}。")
location(("error", 900, "_"))
运行结果
发生错误 900。
Process finished with exit code 0
守护项
我们可以向一个模式添加if
子句,称为“守护项”。 如果守护项为假值,则match
将继续尝试下一个 case
语句块。 请注意值的捕获发生在守护项被求值之前:
match point:
case Point(x, y) if x == y:
print(f"The point is located on the diagonal Y=X at {x}.")
case Point(x, y):
print(f"Point is not on the diagonal.")
•一个简单的例子:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def location(point):
match point:
case Point(x, y) if x == y:
print(f"点位于对角线上的Y=X {x}处。")
case Point(x, y):
print("点不在对角线上。")
location(Point(1, 1))
运行结果
点位于对角线上的Y=X 1处。
Process finished with exit code 0
以上总结或许能帮助到你,或许帮助不到你,但还是希望能帮助到你,如有疑问、歧义,直接私信留言会及时修正发布;非常期待你的点赞和分享哟,谢谢!
未完,待续…
一直都在努力,希望您也是!
微信搜索公众号:就用python
文章作者:李 锋|编辑排版:梁莉莉