一直以来,Python 没有其他语言的 switch 方法来实现多条件分支,要求支持的呼声很高,Python 3.10.0 支持了它,而且是超级版的,实现的思路与它们大有不同。match 与 case 配合,由 case 从上到下将目标与语句中的每个模式进行比较,直到确认匹配,执行相应的模式下的语句。如果未确认完全匹配,则最终用通配符 _(如提供)将用作匹配情况。如所有的都不匹配且没有通配符,则相当于空操作。
模式由序列、映射、基本数据类型以及类实例构成。 模式匹配使得程序能够从复杂的数据类型中提取信息、根据数据结构实现分支,并基于不同的数据形式应用特定的动作。
Python 3.10 实现的 match 功能是其他开发语言中传统的 switch 的超集,它不仅仅是 switch,可以实现更为复杂的功能。模式匹配大大增加了控制流的清晰度和表达能力。
match subject:
case <pattern_1>:
<action_1>
case <pattern_2>:
<action_2>
case <pattern_3>:
<action_3>
case _:
<action_wildcard>
match 语句接受一个表达式并将其值与以一个或多个 case 语句块形式给出的一系列模式进行比较。 具体来说,模式匹配的操作如下:
匹配一个字面值
grade = 3
match grade:
case 1:
print('一年级')
case 2:
print('二年级')
case 3:
print('三年级')
case _:
print('未知年级')
# 三年级
还可以用 | (表示或者)在一个模式中组合几个字面值:
grade = 5
match grade:
case 1:
print('一年级')
case 2:
print('二年级')
case 3:
print('三年级')
case 4 | 5 | 6:
print('高年级')
case _:
print('未知年级')
# 高年级
下边是一利用一个类状态实现的开关功能:
class switch:
on = 1
off = 0
status = 0
match status:
case switch.on :
print('Switch is on')
case switch.off :
print('Switch is off')
守卫 Guard
可以在 case 中编写 if 条件语句,实现与 if 语句类似的功能:
score = 81
match score:
case 100:
print('满分!')
case score if score >= 80:
print(f'高分啊~ {score}')
case score if score >= 60:
print('及格万岁!')
case _:
print('不知道说什么。')
# 高分啊~ 81
在 case 后面可以加入一个 if 判断作为守卫,如匹成功但守卫为假,则继续尝试下一个 case 案例块,值捕获发生在评估守卫之前。再来一个例子:
def fun(score):
match score:
case 100:
print('满分!')
case score if score >= 80:
print(f'高分啊~ {score}')
case score if score >= 60:
print('及格万岁!')
case score if score in [57, 58, 59]:
print('这成绩也是没谁了 ··!')
case _:
print('不知道说什么。')
fun(59)
# 这成绩也是没谁了 ··!
带有字面值和变量的模式
模式可以看起来像解包形式,而且模式可以用来绑定变量。在这个例子中,一个数据点可以被解包为它的 x 坐标和 y 坐标:
# point is an (x, y) tuple
match point:
case (0, 0):
print("坐标原点")
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("未法的坐标数据")
这种情况也可以增加守卫:
point = (60, 0)
match point:
case (0, 0):
print("坐标原点")
case (0, y):
print(f"Y={y}")
case (x, 0) if x > 50:
print(f"X={x},点在 x 轴的远处")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("未法的坐标数据")
# X=60,点在 x 轴的远处
枚举
from enum import Enum
class Color(Enum):
RED = 0
GREEN = 1
BLUE = 2
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 :(")
模式和类
通过类对象可以结构化你的数据,通过使用类名字后面跟一个类似构造函数的参数列表,作为一种模式。这种模式可以将类的属性捕捉到变量中:
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}")
case Point(x=x, y=0):
print(f"X={x}")
case Point(x=x, y=y):
print(f"X={x}, Y={y}")
case Point():
print("这个点不在轴上")
case _:
raise ValueError("未法的坐标数据")
还可以匹配取值:
match media_object:
case Image(type=media_type):
print (f"Image of type {media_type}")
class Direction:
def __init__(self, horizontal=None, vertical=None):
self.horizontal = horizontal
self.vertical = vertical
def 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)
direction(d2)
direction(d3)
还可以利用星号表达式进行解包操作:
# 解析出列表
for thing in [[1,2,3],['a','b','c'],"this won't be matched"]:
match thing:
case [*y]:
print(y)
case _:
print("unknown")
'''
[1, 2, 3]
['a', 'b', 'c']
unknown
'''
匹配数据类型:
for thing in [[1, 2, 3], ['a', 'b', 'c'], "this won't be matched"]:
match thing:
case [int(), int(second), int()] as y:
print(y, f'第二个是{second}')
case _:
print("unknown")
'''
[1, 2, 3] 第二个是2
unknown
unknown
'''
带有位置参数的模式
你可以在某些为其属性提供了排序的内置类(例如 dataclass)中使用位置参数。 你也可以通过在你的类中设置 match_args 特殊属性来为模式中的属性定义一个专门的位置。 如果它被设为 (“x”, “y”),则以下模式均为等价的(并且都是将 y 属性绑定到 var 变量):
Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)
类中设置 __match_args__ 特殊属性为:
class Point:
__match_args__ = ("x", "y")
def __init__(self, x, y):
...
嵌套模式
模式可以任意地嵌套。 例如,如果我们的数据是由点组成的短列表(类似 [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("列表中还有其他内容")
复杂模式和通配符
通配符可以被用在更复杂的模式中,例如 (‘error’, code, _)。 举例来说:
match test_variable:
case ('warning', code, 40):
print("A warning has been received.")
case ('error', code, _):
print(f"An error {code} occurred.")
匹配字典
为了匹配字典,我们对每种情况使用 key:value 模式。在这里,我们检查消息是“成功”还是“失败”,并打印相应的消息:
def check_message(message):
match message:
case {'success': message}:
print(f'Success: {message}')
case {'failure': message}:
print(f'Something wrong: {message}')
case _:
print('Unknown')
message_success = {'success': 'OK!'}
message_failure = {'failure': 'ERROR!'}
check_message(message_success)
check_message(message_failure)
'''
Success: OK!
Something wrong: ERROR!
'''
子模式支持 as
子模式可使用 as 关键字来捕获,如 case (Point(x1, y1), Point(x2, y2) as p2): …。下例:
def alarm(item):
match item:
case [('早上' | '中午' | '晚上') as time, action]:
print(f'{time}好! 是{action}的时间了!')
case _:
print('不是时间!')
alarm(['早上', '吃早餐'])
# 早上好! 是吃早餐的时间了!
可以作为独立语句使用:
thing = 2
match thing:
case 1:
print("thing is 1")
case 2:
print("thing is 2")
case _:
print("thing is other")
可以用用在 for 循环中:
for thing in [1,2,3,4]:
match thing:
case 1:
print("thing is 1")
case 2:
print("thing is 2")
case _:
print("thing is other")
可以用在函数方法中:
def whats_that(thing):
match thing:
case 1:
print("thing is 1")
case 2:
print("thing is 2")
case _:
print("thing is other")
可以嵌套:
def alarm(item):
match item:
case [time, action]:
print(f'Good {time}! It is time to {action}!')
case [time, *actions]:
print('Good morning!')
for action in actions:
print(f'It is time to {action}!')
match 语句将 subject 表达式 与 case 语句中的每个模式(pattern)从上到下进行比较,直到找到匹配的模式。若找不到匹配的表达式,则匹配 _ 通配符(wildcard)(如有),实际上它就是其它语言中的 default 关键字。
支持的有:
其他: