Python中的交换案例等效是什么?

2021-04-04 05:26:04

从历史上看,Python语法Hasn' t有一个切换案例语句。 2006年Guido Van Rossum是Python的原始作者,提出了PEP 3103中的交换案例语法的不同替代方案,但它们似乎都有一些问题,并且想法没有获得足够的流行支持。因此,提案被拒绝了。 Python版本3.10更改了此功能。

模式匹配是Python 3.10和较新的交换案例等效。模式匹配语句从匹配关键字开始,它包含一个或多个案例块。

由于Python 3.9将在2025年10月达到其生命结束以来,任何图书馆代码都可以安全地调整这种新功能,因此需要很长时间。因此,我将这篇文章分成了两部分,取决于您需要的哪个版本目标。在第一部分中我们' ll通过新的模式匹配模型,然后我将展示如何在旧版本中模拟交换机。如果你'唯一对旧方法感兴趣的话。

在进入Python详细信息之前,让' s从实现交换案例语句的另一种语言,c ++。语法很简单,您可以使用交换机启动一个块,使用案例定义条件,并从断裂中退出条件,例如:

开关(i){壳体1:cout

上面的代码使用i的值来决定应在控制台上打印什么。如果没有匹配,则将打印默认值。如果排除Break语句,则执行将继续下一行。

开关(i){壳体1:cout

输入值为1的代码将打印出所有情况。通常,您希望在那里进行中断语句,忘记添加一个可以是代码中错误的源。

正如我已经提到的那样,从Python 3.10开始,您可以使用称为结构模式匹配的功能开始。 PEP 634上有完整规范,可以从PEP 636找到一个友好的介绍。在这里,我试图为您提供快速介绍该概念。

模式Mathcing不仅仅是你的典型交换机,但它实际上比这更强大。但是,您也可以像Switch-case语句一样使用它,这相当于第一个C ++示例:

匹配i:案例1:打印("第一个案例")案例2:打印("第二案例和#34;)案例_:打印(" didn' t匹配案例")

C ++的主要差异是关键字与交换机匹配而不是开关,您不需要使用break以防止代码继续下一个案例,并且默认情况会使用下划线定义,默认情况会始终匹配。

除了匹配字符串文字,数字文字,布尔值等之外,您还可以创建更复杂的捕获模式。您的案例也可以列出或元组,允许您匹配不同的组合。

假设您需要根据获取HTTP方法,目标URL和可能数据的输入,根据输入,您需要执行Web请求。您的功能需要支持Get,Post和Delete,并否则提出错误。具有模式匹配和请求HTTP库的实现可以如下所示(如果我们忘记了请求的存在.Request):

导入请求def do_request(方法,URL,data = none):匹配(方法,数据):案例(" get" none):return请求。重点(url)案例("帖子",_):返回请求.post(url,data = data)案例(" delete" none):return请求.delete(url)案例_:提升valueerror(&#34 ;无效的参数")

该案例基于元组内的值匹配,因此只有允许的参数组合将被执行。如果您尝试将数据添加到其他方法而不是POST,您将获得一个值。

序列的长度也可以变化。这是一个具有不同级别的帮助信息和两个命令的命令行程序的伪结构:

导入sysmain():匹配sys.argv [1:]:案例["帮助"]:打印("打印所有可用选项和#34的一般帮助;)案例["帮助&#34 ;,#34;开始"]:打印("打印开始命令的详细帮助")案例["帮助&#34 ;,#34;停止"]:打印("打印stop命令&#34的详细帮助;)案例["帮助",命令]:打印(f"没有帮助条目命令:{命令}")案例["开始",* args]:打印(f"运行start命令:{args}")案例["停止",* args]:打印(f"运行stop命令:{args}")案例[]:打印("没有命令定义!")案例_:打印("无效的输入")如果_name__ ==" __main__&#34 ;: main()

sys.argv变量包含程序的命令行参数,第一个值始终是运行脚本的名称,因此我们使用列表切片来删除并仅使用剩余的参数。

如果输入参数是帮助,则程序将打印一个可以显示基本使用和可用选项的一般帮助文本。然后键入帮助启动将显示有关启动功能的更详细信息等。

第四种案例捕获有关未知命令的帮助。这与第一项是字符串&#34的两个元素序列匹配;帮助",第二项保存在名为命令的变量中。这真的显示了模式匹配的力量,因为您还可以匹配输入的结构,而不仅仅是值。

启动和停止案例显示如何使用*运算符匹配一系列项目。序列打开包装您可以在序列的任何位置匹配零个或多个项目。只允许一个项目在图案中使用明星。

如果两个开始和停止称为相同的函数,将这些与不同的案例分开时,它不会很有意义。案例模式可以使用|作为或运算符匹配许多不同的值。启动和停止功能可以重新开始:

匹配sys.argv [1:]:......案例[(" start" |" stop")作为命令,* args]:print(f"运行{命令}命令:{args}")...

这里列表中的第一个项目匹配" start"或"停止",然后将其余的命令捕获为序列。 AS关键字将实际匹配的值存储为命令变量,以便我们稍后可以使用它。

此模式具有一个限制:备选方案应绑定相同的变量。所以你不能有一个侧面将值绑定到x,然后尝试在另一侧使用y:

匹配器不限于原始类型和字符串,但它也适用于对象。考虑此简单的用户类,可用于Web服务:

来自Dataclasses Import DataClass @DataClass类用户:ID:int用户名:str电子邮件:str admin:bool区域:str

您可能希望以不同的方式处理管理员用户而不是非管理员用户。此外,也许欧盟居民需要一些特殊的治疗方法:

匹配用户:案例用户(admin = true):打印("这是一个管理员用户")案例用户(eque ="欧盟"):print("这个)是欧盟")案例user():打印("这是一个客户在其他地方")案例_:print("错误:不是用户!&# 34;)

即使案例看起来像是使用不这样的构造函数创建对象。模式匹配使用这些构造函数,如定义,以查找正确的匹配,因此留下未定义的任何属性将作为通配符处理。

因此,第一个案例将匹配管理员的所有用户。第二种案例适用于其区域为"欧盟",第三个案例与剩下的任何用户匹配。当对象不是用户时,使用最后一个情况,但例如无。

到目前为止,我一直在匹配陈述中使用字面价值,但是将它们定义为常量,以'它是更好的?这是你需要小心的地方,以便你不要在你的代码中引入错误!

如果我们从上一部分继续使用用户类,我们可以认为它' S Cleaner以常量处理欧盟特殊情况,以便我们无处不在地使用硬编码值:

在这里,我们达到了捕获变量值的重点。现在,你会期望在北美(" Na")的用户不匹配,而代码会说客户是其他地方。实际发生的是变量区域_eu现在将包含字符串" na"案件会匹配!

这意味着我们也可以从对象捕获变量。所以,如果我们'只有在非管理员用户的区域才能使用这种模式:

匹配user:案例用户(admin = false,区域=区域):打印(F"这是{Region}")案例用户():打印("这是一个admin用户")案例_:打印("错误:不是用户!")

但我们如何使用常量?他们需要是点缀的名称来防止匹配覆盖值。一个选项是使用枚举:

来自Dataclasses Import DataClass from Enum Import Enum Class Region(STR,ENUM):非洲=" AF"亚洲="和#34;欧洲="欧盟" North_america =" na"大洋洲=" OC" south_and_central_america =" SA" @dataClass类用户:ID:int用户名:str电子邮件:str admin:bool区域:区域...匹配用户:案例用户(admin = false,region = region.europe):打印("欧洲客户&# 34;)案例用户(admin = false):打印("其他客户")案例用户(admin = true):print(" admin用户")案例_:print( "错误:不是用户!")

现在你会认为这就是所有人,都有可能有更多的记忆吗?错误的!您还可以在案例中包含条件。

让'■稍微继续使用用户示例。这次我们要处理两个用户在同一区域的情况:

匹配用户:案例[user()作为u1,user()作为u2]如果u1.region == u2.region:print(f"在同一区域中收到的两个用户:{u1.region}&#34 ;)案例[user(),user()]:打印(F"在不同地区的两个用户接收到不同地区")案例_:打印("收到别的东西")

我们可以使用作为关键字的模式中的项目捕获。如果条件,则该模式才会完全匹配,仅当条件表达式也匹配时。如果之前的零件匹配,则在评估条件之前将初始化U1和U2变量。他们将在以后的步骤中仍然可用,因此请注意,即使条件本身不会由于IF-陈述而完全匹配,这也意识到这一点是Ove WOVEWRITE的东西。

如果您的Python版本不支持模式匹配,则需要使用替代方法来模拟切换器。基本上有两种方法可以做到这一点,所以让'看到它们是什么。

最基本且简单的模拟Switch语句的方法是使用普通的陈述。相当于第一个C ++示例将如下所示:

def print_case(value):如果值== 1:打印("第一个案例")Elif值== 2:打印("第二案例")else:print(&# 34; DIDN' t匹配案例")

将该值与每个选项进行比较,直到找到匹配项或达到最终的else语句。

代码很容易理解,但有很多情况,它可能变得难以管理。还有许多状态比较,我们需要手动编写,这可能会出错。但是,这应该是您选择只有几个可能的状态的简单案例。这也是Python文档作为替代品的内容。

实现类似结果的另一种方法是将胼callable放在字典中。该方法也在Python文档中提到。

def print_case(value):案例= {1:lambda:打印("第一个案例"),2:lambda:print("第二案例"),3:lambda:print ("第三案和#34;),4:lambda:print("第四个案例"),}}}}}}}}}}}}}}}}}}}}}} subsice.get(价值,lambda:print(" didn&#39 ; t匹配案例"))()

如您所见,与IF-ELSE语句相比,这可以看起来有点清洁器。

在此代码中,字典中的每个键都包含一个没有参数的Lambda函数,然后调用适当的打印功能。 lambda函数是可以存储在变量中的匿名函数,然后用作正常功能。

使用来自dict的get方法获取正确的函数,然后在没有参数的情况下调用它。 Get方法的第二个参数是如果找不到匹配的情况,则将打印邮件的默认值。

请注意最后一行末尾的括号。这是实际的函数调用。

def print_case(value):案例= {1:lambda:打印("第一个案例"),2:lambda:print("第二案例"),3:lambda:print ("第三案和#34;),4:lambda:print("第四个案例"),}}}}}}}}}案例[value]()

如果您尝试使用无效值调用print_case函数,例如5,则代码将升级键架。这可能是希望的,以便您的程序赢得' t继续输入无效输入。只需务必在代码中的其他地方处理错误。

让&#39改变我们的示例。现在,如果我们想创建一个名为Calculate的函数,何时何地创建一个运算符和两个操作数,并返回计算结果?我们可以这样实施:

def计算(运算符,x,y):案例= {" +&#34 ;: lambda a,b:a + b," - &#34 ;: lambda a,b:a - b," *&#34 ;: lambda a,b:a * b," /&#34 ;: lambda a,b:a / b,}返回案例[运算符](x,y)

每个Lambda函数现在有两个在冒号之前定义的参数。然后,最终函数调用将x和y参数传递给所选的lambda。请注意,Lambda函数Don' t包含return关键字。

def添加(x,y):返回x + y def减法(x,y):返回x - y def乘以(x,y):返回x * y def划分(x,y):返回x / y def计算(运算符,x,y):案例= {" +":添加," - ":减去," *&#34 ;:乘法," /&#34 ;: divide,}返回案例[运算符](x,y)

结果仍然是相同的。这次我们使用正常的命名函数,该函数用作案例查找的对象。

如果您'重新担心性能,那么您可以在函数外移动字典生成。然后,每次调用计算时都会重新生成。在实践中,' t很大差异。

模式匹配是一个全新的野兽来解决,并且起初可能有点恐吓。希望这篇文章在这个主题上揭示了一些亮点,而且你感到舒服地了解更多。如果您需要定位较旧的Python版本,您仍然可以使用我最后描述的替代方案。

什么'新的Päkstech新的? 订阅接收偶尔的电子邮件,我将总结在博客中发生的东西,并且接下来可能会发生什么。