轻量级条件解析与求值表达式的构建

pycond的Python项目详细描述



作者:GK 版本:190523


pycond:轻量级声明性条件表达式

构建状态codecov src=

目录

什么

你有很多数据,可能是流媒体…

id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)

你需要过滤。现在,假设我们已经将它们作为dict列表。

您可以强制执行此操作:

foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]

或者让这个模块从如下声明中组装一个条件函数:

frompycondimportparse_condcond='email contains .de and gender eq Male or last_name eq Scott'is_foo=parse_cond(cond)

然后根据不同的状态/事实/模型(…):

foo_users = [ u for u in users if is_foo(state=u) ]

性能与手工制作的python大致相同(系数2-3)。

< Buff行情>

在现实生活中,由于 pycond的延迟评估功能

为什么

当开发人员决定对数据应用过滤器时,他肯定会 直接使用python优秀的表达能力,如上图所示 通过列表理解。
但是如果过滤条件是基于程序之外的决策 控制?也就是说,来自最终用户,通过网络以某种序列化的形式点击程序,这很少是可直接计算的python。

这是此模块的主要用例。

备选方案

但为什么还要用另一种工具来做这样的标准工作呢?

有一系列很棒的工具和框架,其中条件解析只是其中的一小部分,例如,pyke或durable以及django世界中的许多人或来自sql语句分析器。

1.我只需要一个非常苗条的工具来解析函数,但是这个工具非常透明而且可以定制

pycond允许自定义

  • 条件运算符列表
  • 组合运算符列表
  • 条件算子通过全局或条件局部包装的一般行为
  • 他们的名字
  • 标记器
  • 值查找函数

并作为零依赖单模块发布。

所有评估都是通过部分来完成的,而不是lambdas,也就是说,可以非常简单地通过断点、自定义日志记录运算符或查找包装器对操作进行内省和调试。

2.语法的简单性:易于直接键入,非 程序员也可以从结构化数据(例如从web框架)中进行综合。

3.性能:足以在流过滤器中使用"pyconditions"。 在当前的特性集中,我们有时会差2-3个因子,但(由于延迟评估)通常更好, 与手工制作的列表理解相比。

力学

解析

pycond根据tokenizer函数中提供给解析器的一组约束来解析条件表达式。 标记器的结果将提供给生成器。

importpycondaspccond='[a eq b and [c lt 42 or foo eq bar]]'cond=pc.to_struct(pc.tokenize(cond,sep=' ',brkts='[]'))print(cond)returncond

输出:

[['a', 'eq', 'b', 'and', ['c', 'lt', '42', 'or', 'foo', 'eq', 'bar']]]

建筑

在分析之后,生成器将通过组合运算符组合一组嵌套的运算符函数。 这些函数是部分函数,即尚未计算,但有关必需键的信息已存在 可用:

f,meta=pc.parse_cond('foo eq bar')assertmeta['keys']==['foo']

结构条件

其他进程可以通过可序列化格式(例如json)交付条件结构。 如果您将这些已经标记化的构造交给pycond,那么将绕过标记化器:

cond=[['a','eq','b'],'or',['c','in',['foo','bar']]]assertpc.pycond(cond)(state={'a':'b'})==True

评估

生成器的结果是一个"pycondition",它可以针对系统的不同状态运行多次。 状态的评估方式可在生成和运行时自定义。

默认查找

默认设置是从模块中的初始空状态dict获取表达式中的查找键。

f=pc.pycond('foo eq bar')assertf()==Falsepc.State['foo']='bar'assertf()==True

pycond是不需要元信息时parse-cond的快捷方式)。

通过自定义状态

在求值时使用state参数:

id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
0

深入查找/嵌套状态

您可以提供一个路径分隔符,用于跳入嵌套结构,如so:

id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
1

自定义查找和值传递

您可以提供自己的价值获取功能。

  • 签名:参见示例。
  • 返回:当前状态的键的值加上 比较运算符函数的值。
id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
2

输出:

id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
3 < Buff行情>

如您在示例中所见,state参数只是一个约定 对于pyconds'[标题:默认查找函数,fmatch=pycond.py,lmatch:def state\u get 功能。

延迟评估

这在许多情况下避免了不必要的计算:

当求值分支包含"and"或"and"和"not"组合符时,则 在运行时,我们计算第一个表达式-如果它已经是 错误的。这样就省去了昂贵的深入分支评估,或者 查找是惰性的,甚至不会获取值:

id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
4

输出:

id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
5

建筑条件来自文本

条件函数是从结构化表达式内部创建的- 但这些是难以键入的 包含许多撇号。

基于文本的条件语法适用于最终用户 直接在文本框中键入。

语法

将原子条件与布尔运算符和嵌套括号结合起来,如:

id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
6

原子条件
id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
7
  • 当只给出查找键时,则将co设置为truthy函数:
id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
8

因此,这样的表达式是有效且正确的:

id,first_name,last_name,email,gender,ip_address
1,Rufe,Morstatt,rmorstatt0@newsvine.de,Male,216.70.69.120
2,Kaela,Scott,scott@opera.com,Female,73.248.145.44,2
(...)
9
  • 当给出not lookup_键时,则co设置为错误 功能:
foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
0

条件运算符

所有布尔型标准库运算符 默认情况下可用:

foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
1 <详情>默认支持的nr运算符…(单击以扩展)<表>nr运算符别名添加 和 均衡器 地板 ge>;=gt>;iadd+=着陆 ifloordiv/=ilshift<;=imod%=imul*=ior= 首次公开发行股票 irshift>;=是 不是 isub-=itruediv/=ixor^=le<;=lshift<;lt<;模式%mul*ne!= < < > > >或 功率 rshift>;sub-truediv/xor^项目获取器 长度提示 <表><详细内容><详情>默认支持的str运算符…(单击以扩展)<表> str运算符 吸引子 concat+包含 计数 iconcat+= 索引 方法调用方 <表><详细内容>

使用符号运算符

默认情况下,pycond使用文本样式运算符。

  • Ops_Use_Symbolic仅将整个流程切换为符号样式。
  • ops_use_symbolic_and_txt将processwide切换为允许的两种符号。
foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
2 < Buff行情>

运算符命名空间应在进程开始时分配,它们是全局的。

扩展条件运算符

foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
3

否定

否定条件运算符的结果:

foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
4

反转rev

在调用运算符之前反转参数

foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
5 < Buff行情>

revnot可以按任何顺序组合。

包装条件运算符

全局包装

您可以通过自定义函数全局包装所有评估时间条件操作:

foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
6

您可以通过重复应用run_all ops_throuapi函数来编写这样的包装器。

条件局部包装

这是通过如下所示的Ops_-Thru参数完成的:

foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
7 < Buff行情>

使用ops_thru是调试意外结果的好方法,因为 可以在那里添加断点或记录器。

组合操作

您可以将单个条件与

  • 而不是
  • 是否
  • xor默认情况下。

组合功能存储在pycond.comb-opsdict中,可以扩展。

< Buff行情>

不要对组合运算符的名称使用空格。用户可以使用它们,但它们在标记化时间之前会被替换,例如和not->;和_not

嵌套

组合条件可以使用括号"["和"]"任意嵌套。

< Buff行情>

通过brktsconfig参数,您可以在构建时将它们更改为其他分隔符。

标记化详细信息 < Buff行情>

在这个平面列表格式中,括号作为字符串,例如['[','a','and''b',']'…]

功能

标记器的工作是分离生成器的表达式字符串。

分隔符

分隔表达式的不同部分。默认为""。

foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
8 < Buff行情>

sep可以是任何单个字符,包括二进制字符。

括号字符不需要分隔,标记器将执行以下操作:

foo_users=[uforuinusersif([u['gender']=='Male'oru['last_name']=='Scott')and'@'inu['email'])]
9 < Buff行情>

条件函数本身的计算结果并不相等 已经组装了两次。

撇号

通过将字符串放入撇号中,您可以将记号赋予器告诉n不必进一步检查,例如分离器:

frompycondimportparse_condcond='email contains .de and gender eq Male or last_name eq Scott'is_foo=parse_cond(cond)
0

转义

告诉标记器不要解释下一个字符:

frompycondimportparse_condcond='email contains .de and gender eq Male or last_name eq Scott'is_foo=parse_cond(cond)
1

建筑

autoconv:将值转换成python简单类型

表达式字符串值通过publicpycond.py_type函数自动转换为bools和numbers。

通过将autoconv参数设置为false或使用撇号:

frompycondimportparse_condcond='email contains .de and gender eq Male or last_name eq Scott'is_foo=parse_cond(cond)
2

如果不想提供自定义查找函数(可以在其中执行所需的操作) 但要查找自动转换的键,请使用:

frompycondimportparse_condcond='email contains .de and gender eq Male or last_name eq Scott'is_foo=parse_cond(cond)
3

按需上下文和延迟评估

通常情况下,条件是在用户空间中,应用于 仅在开发时由开发人员控制。

最终用户可能只从API中提供的许多键中选择一些键。

pycond的ctx_builder只允许在运行时计算这些键, 用户决定将条件建立在: 在条件构建时,为all函数移交一个命名空间 可用于构建CTX。

pycon将为您返回一个上下文生成器函数,只调用那些函数 条件实际需要的。

frompycondimportparse_condcond='email contains .de and gender eq Male or last_name eq Scott'is_foo=parse_cond(cond)
4

输出:

frompycondimportparse_condcond='email contains .de and gender eq Male or last_name eq Scott'is_foo=parse_cond(cond)
5

但是我们可以做得更好-我们仍然计算键的值 只在延迟评估的条件的死胡同中需要。

让我们避免计算这些值,记住 自定义查找功能功能。

< Buff行情>

pycond确实很容易为您生成这样的自定义查找函数, 如果将getter命名空间传递为lookup_provider

frompycondimportparse_condcond='email contains .de and gender eq Male or last_name eq Scott'is_foo=parse_cond(cond)
6

输出:

frompycondimportparse_condcond='email contains .de and gender eq Male or last_name eq Scott'is_foo=parse_cond(cond)
7

输出显示,我们甚至没有为条件的死分支调用值提供程序函数。

由pytest2md自动生成,运行pytest2md

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

推荐PyPI第三方库


热门话题
java在使用mutator方法时遇到问题   java应用程序是否连接到服务器   java OutputStreamWriter不重写文件   java如何将捕获的异常与异步任务安卓中的标准异常进行比较?   java初始化和声明对象和方法   用于上传个人资料图片的java ActivityResultLauncher   如何检查Java字符串的第二个数字是否在1到5之间   googleappengine和Java版本   java如何获取eclipse tomcat配置文件夹路径?   在Android上使用GSON时出现java垃圾收集器问题   如何在java中逐层显示图像   JavaSpring数据规范动态条件   java Spring MongoDB在请求时非常慢   java如何在图像滑动视图上进行循环?   java我在函数中有一个空指针异常,不知道如何修复它   通过java运行的Cassandra Talend作业引发错误   java ConcurrentHashMap。initTable(),为什么检查表两次为空?