在 Python 3.10 之前,我们没有任何内置的方式来使用结构模式匹配,在其他编程语言中称为 switch-case。 从 Python 3.10 版本开始,我们不能使用 match ... case
语句来模拟 switch ... case
语句。
本篇文章介绍结构模式匹配及其在 Python 中的重要性。 它还使用不同的模式来演示如何使用 match … case 语句。
截至 2021 年初,我们无法在低于或等于 3.9 的已发布 Python 版本中使用 match 关键字。 那时,我们习惯于使用字典或嵌套的 if/elif/else 语句来模拟 switch ... case
。
但是,Python 3.10 引入了一个新特性,称为结构模式匹配(match ... case
语句)。 它相当于我们在 Java、C++ 和许多其他编程语言中使用的 switch ... case
语句。
这一新功能使我们能够编写简单、易于阅读且最不容易出错的流程控制语句。
结构模式匹配用作 switch … case 语句,比这更强大。 如何? 让我们探索下面的一些示例,以了解它们在不同情况下的用途。
示例代码:
colour = "blue"
match colour:
case "green":
print("The specified colour is green")
case "white":
print("Wow, you've picked white")
case "green":
print("Great, you are going with green colour")
case "blue":
print("Blue like sky...")
输出:
Blue like sky...
在这里,我们首先有一个包含蓝色的可变颜色。 然后,我们使用 match 关键字,它将颜色变量的值与各种指定的案例进行匹配,其中每个案例都以 case 关键字开头,后跟我们要比较或检查的模式。
模式可以是以下之一:
match ... case
语句只运行第一个匹配的 case 下的代码。
如果没有匹配到 case 怎么办? 用户将如何知道它? 为此,我们可以有一个默认情况,如下所示。
示例代码:
colour = "yellow"
match colour:
case "green":
print("The specified colour is green")
case "white":
print("Wow, you've picked white")
case "green":
print("Great, you are going with green colour")
case "blue":
print("Blue like sky...")
case other:
print("No match found!")
输出:
No match found!
示例代码:
student = {
"name": {"first": "Mehvish", "last": "Ashiq"},
"section": "B"
}
match student:
case {"name": {"first": firstname}}:
print(firstname)
输出:
Mehvish
在上面的示例中,结构模式匹配在以下两行代码中起作用:
match student:
case {"name": {"first": firstname}}:
我们使用 match … case 语句通过从学生数据结构中提取学生的名字来找到它。 在这里,student 是一个包含学生信息的字典。
案例行指定我们的模式来匹配学生。 考虑到上面的例子,我们寻找一个带有“name”键的字典,其值为一个新字典。
这个嵌套字典包含一个“first”键,其值绑定到 firstname 变量。 最后,我们使用 firstname 变量来打印值。
如果您更深入地观察它,我们已经了解了这里的映射模式。 如何? 映射模式看起来像 {"student": s, "emails": [*es]}
,它匹配映射与至少一组指定的键。
如果所有子模式都匹配它们对应的值,那么它将在与键对应的值匹配期间绑定任何子模式绑定。 如果我们想要允许捕获额外的项目,我们可以在模式的末尾添加 **rest。
示例代码:
def sum_list_of_numbers(numbers):
match numbers:
case []:
return 0
case [first, *rest]:
return first + sum_list_of_numbers(rest)
sum_list_of_numbers([1,2,3,4])
输出:
10
这里,我们使用递归函数,使用捕获模式捕获到指定模式的匹配,并绑定到名字上。
在此代码示例中,如果第一种情况与空列表匹配,则第一种情况返回 0 作为总和。 第二种情况使用带有两个捕获模式的序列模式来匹配列表与多个项目/元素之一。
在这里,列表中的第一项被捕获并绑定到名字,而第二个捕获模式 *rest
使用解包语法来匹配任意数量的项目/元素。
%> 请注意
,其余部分绑定到包含数字的所有项目/元素的列表,不包括第一个。 为了获得输出,我们通过传递上面给出的数字列表来调用 sum_list_of_numbers()
函数。
示例代码:
def sum_list_of_numbers(numbers):
match numbers:
case []:
return 0
case [first, *rest]:
return first + sum_list_of_numbers(rest)
case _:
incorrect_type = numbers.__class__.__name__
raise ValueError(f"Incorrect Values. We Can only Add lists of numbers,not {incorrect_type!r}")
sum_list_of_numbers({'1':'2','3':'4'})
输出:
ValueError: Incorrect Values. We Can only Add lists of numbers, not 'dict'
我们在学习 match ... case
语句的基本用法时学习了使用通配符模式的概念,但没有在那里介绍通配符模式术语。 想象一下前两种情况不匹配的情况,我们需要一个 catchall 模式作为我们的最终情况。
例如,如果我们得到任何其他类型的数据结构而不是列表,我们希望引发错误。 在这里,我们可以使用 _ 作为通配符模式,它将匹配任何内容而不绑定到名称。 我们在最后一个案例中添加错误处理以通知用户。
你怎么说? 我们的模式适合搭配吗? 让我们通过传递字符串值列表来调用 sum_list_of_numbers() 函数来测试它,如下所示:
sum_list_of_numbers(['1','2','3','4'])
它将产生以下错误:
TypeError: can only concatenate str (not "int") to str
因此,我们可以说该模式仍然不够万无一失。 为什么? 因为我们将列表类型数据结构传递给 sum_list_of_numbers() 函数,但具有字符串类型值,而不是我们预期的 int 类型。
请参阅以下部分以了解如何解决它。
示例代码:
def sum_list_of_numbers(numbers):
match numbers:
case []:
return 0
case [int(first), *rest]:
return first + sum_list_of_numbers(rest)
case _:
raise ValueError(f"Incorrect values! We can only add lists of numbers")
sum_list_of_numbers(['1','2','3','4'])
输出:
ValueError: Incorrect values! We can only add lists of numbers
基本情况(第一种情况)返回 0; 因此,求和仅适用于我们可以用数字相加的类型。 请注意,Python 不知道如何添加文本字符串和数字。
所以,我们可以使用类模式来限制我们的模式只匹配整数。 类模式类似于映射模式,但匹配属性而不是键。
示例代码:
def sum_list_of_numbers(numbers):
match numbers:
case []:
return 0
case [int(first) | float(first), *rest]:
return first + sum_list_of_numbers(rest)
case _:
raise ValueError(f"Incorrect values! We can only add lists of numbers")
假设我们想让 sum_list_of_numbers()
函数用于值列表,无论是 int 类型还是 float 类型的值。 我们使用用竖线符号 (|) 表示的 OR 模式。
如果指定列表包含 int 或 float 类型值以外的值,则上述代码必须引发 ValueError。 让我们考虑以下所有三种情况进行测试。
测试 1:传递具有 int 类型值的列表:
sum_list_of_numbers([1,2,3,4]) #output is 10
测试 2:传递具有 float 类型值的列表:
sum_list_of_numbers([1.0,2.0,3.0,4.0]) #output is 10.0
测试 3:传递具有除 int
和 float
类型之外的任何其他类型的列表:
sum_list_of_numbers(['1','2','3','4'])
#output is ValueError: Incorrect values! We can only add lists of numbers
如您所见,由于使用了 OR 模式,sum_list_of_numbers() 函数适用于 int 和 float 类型的值。
示例代码:
def say_hello(name):
match name:
case "Mehvish":
print(f"Hi, {name}!")
case _:
print("Howdy, stranger!")
say_hello("Mehvish")
输出:
Hi, Mehvish!
此示例使用与文字对象匹配的文字模式,例如,显式数字或字符串,正如我们在学习 match ... case
语句的基本用法时所做的那样。
它是最基本的模式类型,让我们模拟一个类似于 Java、C++ 和其他编程语言的 switch ... case
语句。 您可以访问此页面以了解所有模式。