python中的模式匹配

pyPMatch的Python项目详细描述


PypMatch

< Buff行情>

这是正在进行的工作!预计会出现一些粗糙点,但仍会缺少一些功能。

pypmatchpython中提供模式匹配。它主要基于模式匹配 scala。它的主要目标是解构对象,从而检查是否有 给定的对象满足以特定方式解构的条件。

本文档对pypmatch及其功能进行了粗略的、未经润色的概述。可以找到更好的文档 在文档-文件夹中,特别是简介.下面还有一个常见问题解答。

PypMatch至少需要Python3.4。

示例

pypmatch最初是通过其抽象语法树(ast)为分析python代码而开发的。下面的例子 演示如何使用pypmatch的模式匹配来实现非常简单的代码优化程序。但是,没有什么 从pypmatch的角度来看,关于ast-模块的特殊性,您可以将它与任何 否则,

importastfromastimportAdd,BinOp,Numdefsimplify(node):matchnode:caseBinOp(Num(x),Add(),Num(y)):returnNum(x+y)caseBinOp(Num(n=x),Sub(),Num(n=y)):returnNum(x-y)caseast.UnaryOp(ast.USub(),x@Num()):returnNum(-x.n)case_:returnnode

您将在"示例"文件夹中找到更多示例;只需运行"运行示例"即可。

文档文件夹中也有一些文档,特别是介绍

用法

安装PypMatch

要安装PypMatch库,请执行以下简单操作:

pip install pyPMatch

直接编译/执行代码

如果您只想在试驾时使用pypmatch,请使用下面所示的pyma_exec

frompmatchimportpama_execmy_code="""from random import randintmatch randint(0, 19):    case 0:        print("nothing")    case 1 | 4 | 9 | 16:        print("a square")    case 2 | 3 | 5 | 7 | 11 | 13 | 17 | 19:        print("a prime")    case _:        print("some other number")"""pama_exec(my_code)

同样可以使用函数pama_translate

检索并检查由pypmatch生成的代码
frompmatchimportpama_translatemy_code="""from random import randintmatch randint(0, 19):    case 0:        print("nothing")    case 1 | 4 | 9 | 16:        print("a square")    case 2 | 3 | 5 | 7 | 11 | 13 | 17 | 19:        print("a prime")    case _:        print("some other number")"""code,match_code=pama_translate(my_code)print(code)# the translation of the original codeprint("="*80)print(match_code)# additional code for the actual matching

从python模块导入代码

安装自动导入钩子可能更方便,因此包/项目中的所有模块 使用pypmatch-编译器编译(如果它们包含case语句,即)。已安装自动导入 直接通过导入启用自动导入

frompmatchimportenable_auto_importfromrandomimportrandintimportmy_modulemy_module.test_me(randint(0,19))

然后,my_module.py的内容类似于:

deftest_me(arg):matcharg:case0:print("nothing")case1|4|9|16:print("a square")case2|3|5|7|11|13|17|19:print("a prime")caseint():print("some other number")case_:print("please provide an integer")

装饰功能

如果您不希望pypmatch干扰您的代码,您仍然可以使用函数形式的模式匹配 装饰师。你把这个图案作为一根绳子放进装饰器里。然后,函数本身接受 模式作为参数。

frompmatchimportcase@case("17")deftest_me():print("This is correct!")@case("11 | 13 | 17 | 19")deftest_me():print("At least, it's still a prime number")@case("i @ int()")deftest_me(i):print("The result",i,"is wrong")@case("x")deftest_me(x):print("Not even an integer?",x)test_me(sum([2,3,5,7]))

nb:毕竟,使用decorators对于这个库不是一个特别好的主意。原因是,在 与预编译模块不同,并非所有名称都可以正确解析。因此你可能会得到一些 惊喜,甚至崩溃。

如何编写模式

模式可以使用下面描述的元素来表示。

< Buff行情>

如上所述:并不是所有的东西都得到了充分的实现和测试!特别是,只有有限的 目前支持a+b

  • foo()匹配类的所有实例
  • foo(a,b,c)解构foo的实例,该实例必须产生三个值,然后必须与模式匹配 abc;分别是
  • foo(egg=a,ham=b)匹配foo的所有实例,其中属性eggham匹配模式 ab分别;
  • 12'abc'true和其他常量如果值等于常量,则匹配一个值;
  • {a':a,'b':b}如果值有元素'a',则匹配,并且元素'b'匹配a并且 分别是。值可以是dictionary,但不必是。您还可以查看 列表中的元素,例如,使用{2:a,5:b}
  • {'re}如果值是符合给定正则表达式的字符串,则匹配;
  • {foo}匹配字符串类型的任何值v,对于该值,v.isfoo()的计算结果为true。例如,{lower} 将匹配v.islower()为真的任何字符串;
  • a b c如果至少有一种模式匹配,abc匹配;
  • [a,b,c,…,d,e]匹配前三个元素匹配abc的任何序列以及最后两个元素 元素分别匹配de。这还包括python常用的迭代器解包,例如 [a,b,*c,d],解释为[a,b,c@…,d]
  • a+b如果字符串可以分解为ab部分,则匹配该字符串。例如,'('+x+')'匹配 任何在括号中包含一些文本的字符串,并将中间部分返回为x
  • x@a如果模式a匹配,则匹配;如果整个匹配是 成功;
  • 是匹配所有内容的通配符;
  • *\u..是序列中使用的通配符,通常具有完全相同的含义;
  • xx@/code>的缩写,匹配所有内容,并将其绑定到x

有些特殊情况和限制您应该注意:

  • 任何变量x只能在单个模式(acase语句)中绑定一次。再利用是合法的 x在不同的case语句中,但不能有类似于foo(x,x)的语句。如果你需要测试 foo中的值相等,如果x==y则改用foo(x,y);
  • 不能将任何内容绑定到替代项中。因此,a(x@b)c是非法的;
  • 无法将任何内容绑定到通配符。虽然\u是python中的常规名称,但它有特殊的 在pypmatch模式中的含义。然而,类似于@a的内容并不非法,而是等同于a()
  • 即使省略号..在python中是一个"正常值",但它在pypmatch中作为通配符有特殊的含义;
  • 如果您想确保您有一个带有特定键/值的字典,{…}不够。使用 语法dict({'key':值,…})取而代之;
  • 您可以使用{int}{float}检查字符串值,而不是自己编写正则表达式。 分别包含intfloat
  • PypMatch不查看涉及的名称。如果名称后面有括号,如foo()所示,则取该名称 引用对其值进行测试的类/类型。否则,名称是与任何 价值。这意味着模式str将匹配所有内容并覆盖流程中的变量str, 而str()将测试值是否为字符串;
  • 最后一条规则有几个例外。因为名称绑定在其他选项中是非法的,你可以写 a b c作为a()b()c()的缩写。此外,x@a被解释为x@a(),因为它使 将两个不同的变量绑定到完全相同的值;
  • 由于变量的形式不能是a.b,因此属性a.b本身相当于a.b()
  • <代码>3…| 6是序列3 4 5 6的缩写。此语法可用于整数和字符 (单字符串)。因此,您还可以编写'a'…|"z",例如。注意,这里你需要 写省略号,不能使用其他等价的标记

路线图
  • 完全支持正则表达式和字符串匹配
  • 测试套件
  • 文档、教程

case语句的两个版本

有两种版本的case语句。您可以在match块中使用case或作为独立的 声明。

match块中,与模式进行比较,由match指定。

deffoo(x):matchx:case'a'|...|'z':print("Lowercase letter")case'0'|...|'9':print("Digit")case_:print("Something else")

同样的内容也可以在没有匹配的情况下编写。在这种情况下,需要指定要测试的值 模式。这是使用作为语法完成的。不过,这是有区别的。独立的case语句将 所有这些都要经过测试,因此我们明确需要使用return以避免对所有内容打印"其他内容"

deffoo(x):casexas'a'|...|'z':print("Lowercase letter")returncasexas'0'|...|'9':print("Digit")returncasexas_:print("Something else")

目前,您不能将独立的case放在match块中,当然,如果没有 指定a匹配块之外的值。

FAQ

我可以在我的项目中使用pypmatch吗?

是的,pypmatch是在Apache2.0许可下发布的,它应该允许您在您的 自己的项目。由于项目目前正在进行大量开发,模式匹配可能会在意外情况下失败 不过,是的。

为了为模式匹配提供这种新语法,pypmatch需要在python自己的代码之前翻译代码 解析器/编译器可以触摸它。但是,翻译过程的目的是只修改原始文档的最小值 python代码。不删除任何赞扬,不插入或删除任何行,也不重命名任何变量或函数。但自从 casematch已成为关键字,可能与现有代码不兼容。

除了casematch之外,PypMatch还引入了另外两个名称:分别是\u match\u matchvalue。 但是,您的程序不太可能使用这两个名称中的任何一个。

为什么还有另一个模式匹配库/方案?

以前曾讨论过添加switch语句,甚至与python匹配的模式(参见,例如, pep 3103)。因此,pypmatch不是一个新的想法。与大多数 到目前为止,我知道这个项目的不同之处在于,我的重点不在于确切的语法,而在于 语义正确。最后,我只需要(或者说是"强烈要求")模式匹配 对于我正在进行的其他项目。

因此,pypmatch显示了完整模式匹配是如何与python集成的,但是没有任何声明 这里使用的语法是最好的选择。

为什么不直接使用正则表达式呢?

如果你想匹配一个字符串,比如说,正则表达式是很好的。不过,我们在这里提供的模式匹配, 研究基因ral python对象,而不是字符串。它更类似于isinstancehasattr 用python进行测试。

如何检查值是否具有特定类型?

由于python的语法,为了指定s应该是str类型,像s:str这样的东西将不起作用。 在python中通常要做的是isinstance(value,str),它直接转换为:

importastfromastimportAdd,BinOp,Numdefsimplify(node):matchnode:caseBinOp(Num(x),Add(),Num(y)):returnNum(x+y)caseBinOp(Num(n=x),Sub(),Num(n=y)):returnNum(x-y)caseast.UnaryOp(ast.USub(),x@Num()):returnNum(-x.n)case_:returnnode
0

请确保将括号放在str之后,因为这些括号告诉pypmatch应该是 要测试的类,而不是值的新名称。

如何检查值是否具有特定属性?

如果不关心对象的类或类型,而只关心其属性,请使用通配符 类名。然后,算法将省略isinstance检查,并只测试对象的属性是否满足 给定的条件-在本例中,就是有一个属性egg,它可以是任何东西。

importastfromastimportAdd,BinOp,Numdefsimplify(node):matchnode:caseBinOp(Num(x),Add(),Num(y)):returnNum(x+y)caseBinOp(Num(n=x),Sub(),Num(n=y)):returnNum(x-y)caseast.UnaryOp(ast.USub(),x@Num()):returnNum(-x.n)case_:returnnode
1

上面的示例将被转换为对hasattr(value,'egg')格式的简单测试

我可以嵌套match/case结构吗?

基本上,是的,你可以。这里唯一真正的限制是不能将匹配直接放在另一个 匹配,而将匹配放入案例中则没有问题。也就是说,以下操作将失败:

importastfromastimportAdd,BinOp,Numdefsimplify(node):matchnode:caseBinOp(Num(x),Add(),Num(y)):returnNum(x+y)caseBinOp(Num(n=x),Sub(),Num(n=y)):returnNum(x-y)caseast.UnaryOp(ast.USub(),x@Num()):returnNum(-x.n)case_:returnnode
2

其原因是match将表达式的值x放入一个局部变量中(并且 记账)。第二个match把这本书弄糟了,并用y替换了x,这样后续的测试就失败了。 另一方面,几乎没有任何理由可以解释为什么在另一个匹配项中的匹配项应该有意义。

不过,目前还没有完全实现嵌套。只要把match/case结构分开 功能,永远不会有问题。

这个模式匹配库有效吗?

这个库的主要目标是正确性,而不是效率。一旦一切顺利,还有时间 担心提高图书馆的性能。但是,在效率方面有一些很强的限制 模式匹配可以在python中完成。

由于匹配算法必须分析各种对象和类,因此每次执行匹配时,都有 当然,模式匹配算法在python中可以提供的性能限制。如果你有什么东西 与下面的代码片段一样,如果my_valuefoo的实例,则该算法必须进行测试(至少) 属性eggsham,如果属性eggs的值是123

importastfromastimportAdd,BinOp,Numdefsimplify(node):matchnode:caseBinOp(Num(x),Add(),Num(y)):returnNum(x+y)caseBinOp(Num(n=x),Sub(),Num(n=y)):returnNum(x-y)caseast.UnaryOp(ast.USub(),x@Num()):returnNum(-x.n)case_:returnnode
3

在静态编译语言中,如果类foo具有属性,则只能测试一次(在编译期间) 鸡蛋火腿。然而,在python中,甚至引用的类foo也可能更改,因此我们需要测试所有内容 每次匹配时。

另一个限制是由于pypmatch试图最小化需要更改的代码量。这意味着 每个case语句都是与所有其他语句隔离处理的,因此不可能排除 共同的部分。同样,当然还有进一步改进的余地,但这不是pypmatch的优先事项。

如果我使用casematch作为变量名,它会破坏我的代码吗?

当然,总是存在这样的危险,即pypmatch的编译器会错误地将某个变量标识为match, 或case语句。然而,我为了被识别为语句,关键字(casematch)必须是 一行中的第一个单词,不能后跟冒号或运算符(如赋值)。所以,如果你有 一个名为case的函数,该函数调用case(…)可以解释为一个case语句,但是赋值 例如,case=…,比如,will not.

为什么对名称绑定使用@而不是:=

python 3.8将引入赋值表达式(请参见pep 572)。它会 因此,很自然地使用x:=a而不是x@a进行名称绑定。

事实上,我很高兴添加对:=的完全支持。但是,在编写时,:=还不是 蟒蛇。仅使用:=意味着PypMatch至少需要Python3.8,而@已经成为有效的 python 3.5中的operatorpep 465

为什么1…| 9而不是更简单的1…9 < /代码>?

pypmatch中模式的整个语法都基于标准的python语法。即使模式是语义上的 胡说,它们在语法上是有效的。序列1…但是,9在python中不是有效的序列,并且 发出语法错误。

希望模式成为有效的python语法有多种原因。其中之一就是皮普马奇 更不用说独立分析了。

除了这个语用学问题外,写作〈代码〉1…| 9对我来说似乎更清楚,因为1…9也可能意味着 值必须是序列1、2、3、…、9本身。然而,这是个人品味的问题,因此 值得商榷。

为什么有两种版本的case语句?

模式匹配通常不仅以块的形式出现。有时,我们只想解构 单一价值。python已经通过a、b、*c=x等赋值部分地支持这一点。使用独立的 在case的版本中,您可以用case x的形式(a,b,*c):来编写它。但是,case语句可以做很多事情 不仅仅是python的赋值运算符。

另一方面,在开发这个库时,我想知道是否有可能赋予case含义,即使在 匹配块,以便使整个语法尽可能正交和灵活。

由于pypmatch是一种原型,最后,case的独立变体可能无法生存,也无法使其成为 后续版本。目前,它仍在那里,以充分测试其有用性。

为什么match不是scala中的表达式?

scala的语法和语义是基于表达式的,而python的语法和语义则不是。复合语句,如whileiffor等实际上从来不是python中的表达式,而是没有正确值的语句。 由于这里实现的matchcase语句显然都是复合语句,因此感觉非常 python尝试并使它们成为表达式是错误的。

为什么我必须使用case而不是else

pypmatchem>的实现侧重于最小化对任何python代码或模块的重写。它只会 translatecase,andmatch语句,其中可以确定这样的语句在第一个语句中 Place,让所有代码都不受影响。

如果我们要使用else,这意味着我们将不得不在努力确保没有else 替换了它应该保留的位置,导致代码更长更复杂。此外,个别case语句 在match块中,实际上并没有链接,而是作为单独的语句。使用else引发 因此,还需要回答一些有关语义的附加问题。

因此,简而言之:使用else将导致更脆弱的语法,其中有相当多的角案例没有涉及。

一些适当的文档如何?

当前的首要任务是使库完全运行,并添加各种测试用例。曾经 完成后,文档将继续(毕竟,已经有一个相当长的包含大量信息的自述文件, 以及几个例子)。如果您有特定的问题或问题,请打开一个问题,或直接写信给我。

贡献者

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java ParsePushReceiver参数   java如何从设备读取完整数据?   java将java_设置为home,但忽略错误   java如何从歌曲中对专辑进行排序?   java libnaude+windows 10 x64+Eclipse   java如何将maven目标更改为如图所示的想法中的quickicon?   java swing布局中心面板,可滚动显示多个窗口窗格   使用MOSQUITO代理的mqtt中的java SSL   java如何通过属性值获取XML字符串   java在服务器每次启动时停止GWT编译   java如何让javac搜索类路径的子目录?   可以比较java中的两个不同类吗?   JAVAAndroid活动内部类中的lang.NoClassDefFoundError   java HttpServletRequest获取请求头参数块   C++socket与java客户端的连接   java如何在Apache commons http客户端上使用SSL客户端证书   使用预编译正则表达式模式提高java速度   JavaRhino将两个已编译脚本合并为一个脚本