做这个规则引擎的初衷是用来实现一个可序列号为json,容易拓展的条件执行引擎,用在类似工作流的场景中,最终实现的效果希望是这样的:

简单整理下需求

  • 执行结果最终返回=true= or false
  • 支持四则运算,逻辑运算以及自定义函数等
  • 支持多级规则组合,级别理论上无限(Python递归调用深度限制)
  • 序列化成json

实现

json没有条件判断和流程控制,且不可引用对象,是不好序列化规则的,除非用树来保存,但这样又过于臃肿不好阅读。

在苦苦思索的时候,突然灵光一闪~曾经我用过一个自动装机系统—razor,

它使用一种tag语法来匹配机器并打标签,他的语法是这样的:

  1. ["or"
  2. ["=" ["fact" "macaddress"], "de:ea:db:ee:f0:00"]
  3. ["=" ["fact" "macaddress"], "de:ea:db:ee:f0:01"]]

这表示匹配目标机器的Mac地址等于=de:ea:db:ee:f0:00=或=de:ea:db:ee:f0:00=,这种表达既简洁,又足够灵活这种灵活体现在理论上可以无限嵌套,也可以随意自定义操作函数(这里的=、fact)

这灵感来自于古老的=Lisp=,完全可以实现我们的想法~并且简单、好用,还非常非常灵活!就它了!

因此我就使用这种基于=Json Array=的语法来实现我们的规则引擎。

最后实现的语法规则是这样的:

规则语法 基本语法: [“操作符”, “参数1”, “参数2”, …]

多条判断语句可组合,如:

  1. ["操作符"
  2. ["操作符1" "参数1" "参数2" ...],["操作符2" "参数1" "参数2" ...]
  3. ]
  4. ["and"
  5. [">" 0 0.05],
  6. [">" 3 2]
  7. ]

支持的操作符: 比较运算符:

  1. =, !=, >, <, >=, <=

逻辑运算符:

  1. and or not in

四则运算:

  1. +, -, *, /

数据转换:

  1. int str upper lower

其他特殊操作符:

  1. 可自定义操作符,例如get,从某http服务获取数据

代码

  1. class RuleParser(object):
  2. def __init__(self rule):
  3. if isinstance(rule basestring):
  4. self.rule = json.loads(rule)
  5. else
  6. self.rule = rule
  7. self.validate(self.rule)
  8. class Functions(object):
  9. ALIAS = {
  10. '=' 'eq'
  11. '!=' 'neq'
  12. '>' 'gt'
  13. '>=' 'gte'
  14. '<' 'lt'
  15. '<=' 'lte'
  16. 'and' 'and_'
  17. 'in' 'in_'
  18. 'or' 'or_'
  19. 'not' 'not_'
  20. 'str' 'str_'
  21. 'int' 'int_'
  22. '+' 'plus'
  23. '-' 'minus'
  24. '*' 'multiply'
  25. '/' 'divide'
  26. }
  27. def eq(self *args):
  28. return args[0] == args[1]
  29. def neq(self *args):
  30. return args[0] != args[1]
  31. def in_(self *args):
  32. return args[0] in args[1:]
  33. def gt(self *args):
  34. return args[0] > args[1]
  35. def gte(self *args):
  36. return args[0] >= args[1]
  37. def lt(self *args):
  38. return args[0] < args[1]
  39. def lte(self *args):
  40. return args[0] <= args[1]
  41. def not_(self *args):
  42. return not args[0]
  43. def or_(self *args):
  44. return any(args)
  45. def and_(self *args):
  46. return all(args)
  47. def int_(self *args):
  48. return int(args[0])
  49. def str_(self *args):
  50. return unicode(args[0])
  51. def upper(self *args):
  52. return args[0].upper()
  53. def lower(self *args):
  54. return args[0].lower()
  55. def plus(self *args):
  56. return sum(args)
  57. def minus(self *args):
  58. return args[0] - args[1]
  59. def multiply(self *args):
  60. return args[0] * args[1]
  61. def divide(self *args):
  62. return float(args[0]) / float(args[1])
  63. def abs(self *args):
  64. return abs(args[0])
  65. @staticmethod
  66. def validate(rule):
  67. if not isinstance(rule list):
  68. raise RuleEvaluationError('Rule must be a list, got {}'.format(type(rule)))
  69. if len(rule) < 2
  70. raise RuleEvaluationError('Must have at least one argument.')
  71. def _evaluate(self rule fns):
  72. """
  73. 递归执行list内容
  74. """
  75. def _recurse_eval(arg):
  76. if isinstance(arg list):
  77. return self._evaluate(arg fns)
  78. else
  79. return arg
  80. r = map(_recurse_eval rule)
  81. r[0] = self.Functions.ALIAS.get(r[0]) or r[0]
  82. func = getattr(fns r[0])
  83. return func(*r[1:])
  84. def evaluate(self):
  85. fns = self.Functions()
  86. ret = self._evaluate(self.rule fns)
  87. if not isinstance(ret bool):
  88. logger.warn('In common usage, a rule must return a bool value,'
  89. 'but get {}, please check the rule to ensure it is true' )
  90. return ret

解析

这里Functions这个类,就是用来存放操作符方法的,由于有些操作符不是合法的Python变量名,所以需要用ALIAS做一次转换。

当需要添加新的操作,只需在Functions中添加方法即可。由于始终使用array来存储,所以方法接收的参数始终可以用args[n]来访问到,这里没有做异常处理,如果想要更健壮的话可以拓展validate方法,以及在每次调用前检查参数。

整个规则引擎的核心代码其实就是=evaluate=这个10行不到的方法,在这里会递归遍历列表,从最里层的列表开始执行,然后层层往外执行,最后执行完毕返回一个Boolean值,当然这里也可以拓展改成允许返回任何值,然后根据返回值来决定后续走向,这便可以成为一个工作流中的条件节点了。

结束语

东西简单粗陋,希望能给大家带来一些帮助或者一些启发~

教你一招 | 用Python实现简易可拓展的规则引擎的更多相关文章

  1. 【转载】教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神

    原文:教你分分钟学会用python爬虫框架Scrapy爬取心目中的女神 本博文将带领你从入门到精通爬虫框架Scrapy,最终具备爬取任何网页的数据的能力.本文以校花网为例进行爬取,校花网:http:/ ...

  2. IE-“无法浏览网页” 教你十招解决疑难杂症

    “无法浏览网页” 教你十招解决疑难杂症 相信大家也有遇到过像IE不能上网浏览的问题.下面就来给大家介绍一下常见原因和解决方法: 一.网络设置的问题 这种原因比较多出现在需要手动指定IP.网关.DNS服 ...

  3. 教你一招 - 如何安装nopcommerce2.5

    教你一招 - 如何安装nopcommerce2.5 29. 五月 2012 16:22         /          wcf         /          教你一招 . 解决方案    ...

  4. 文章如何做伪原创 SEO大神教你几招做"原创"网站文章的心得

    想要创作出好的文章并被百度所喜欢,就非常需要SEO的优化能力,以及要对文章进行塬创或伪塬创,那么,如何做伪塬创文章?以及如何做好塬创网站文章呢?对此,本文小编就为大家带来了几招做"塬创&qu ...

  5. python搭建简易服务器实例参考

    有关python搭建简易服务器的方法. 需求分析: 省油宝用户数 已经破了6000,原有的静态报表 已经变得臃肿不堪, 每次打开都要缓上半天,甚至浏览器直接挂掉 采用python搭建一个最最简易的 w ...

  6. PDF怎么替换页面,教你一招秒实现

    PDF格式是在办公中比较常用的文件格式之一,虽然很好用,也很容易携带,但也容易出现一个问题,当你想要对PDF文件操作或者修改的时候,才发现PDF文件不是那么容易就能进行编辑和修改的,特别是需要对PDF ...

  7. 线上Bug无法复现怎么办?老司机教你一招,SpringBoot远程调试不用愁!

    前言 在部署线上项目时,相信大家都会遇到一个问题,线上的 Bug 但是在本地不会复现,多么无奈. 此时最常用的就是取到前端传递的数据用接口测试工具测试,比如 POSTMAN,复杂不,难受不? 今天陈某 ...

  8. 教你用OpenCV 和 Python给证件照换底色(蓝底 <->红底->白底)

    在我们的生活中常常要用到各种底色要求的证件电子照,红底.蓝底.或者白底,而假如你手上只有一种底色的证件照,你又不想再去拍又不会PS怎么办?今天教你们用OpenCV和Python给你的证件照换底色. P ...

  9. Python编写简易木马程序(转载乌云)

    Python编写简易木马程序 light · 2015/01/26 10:07 0x00 准备 文章内容仅供学习研究.切勿用于非法用途! 这次我们使用Python编写一个具有键盘记录.截屏以及通信功能 ...

随机推荐

  1. C#语言介绍

    C#(读作“See Sharp”)是一种简单易用的新式编程语言,不仅面向对象,还类型安全. C# 源于 C 语言系列,C.C++.Java 和 JavaScript 程序员很快就可以上手使用. C# ...

  2. 软件开发架构介绍||OSI七层协议之物理层、数据链路层、网络层、传输层(mac地址、ip协议、断开协议、tcp协议之三次握手四次挥手)

    一.网络编程 软件开发架构 C/S架构 C:客户端 想体验服务的时候才会去找服务端体验服务 S:服务端   24小时不间断的提供服务,即时监听,随时待命 B/S架构 B:浏览器    想体验服务的时候 ...

  3. JavaScript函数定义 ,参数调用

    一.JavaScript函数函数: 函数就是一种封装,由事件驱动的或者当它被调用时执行的可重复使用的代码块.定义函数:function 函数名(){函数体;}数不会自动执行,需要被调用才可以执行函数名 ...

  4. Api管家系列(二):编辑和继承Class

    上篇写了个大概,今天我详细说一下参数的编辑,废话不多说 先打开一个项目,我要特别说一下设置里的“默认参数设置” 打开默认参数设置,这里我用红色圈出的tab可以设置请求头,返回头和返回状态,这些设置会在 ...

  5. Ftp修改为主被动模式命令

    FTP是有两种数据连接模式的,主动模式和被动模式. PORT(主动)方式:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路.当需要传送数据时,客户端在命令链路上用 ...

  6. 搭建环境-Monkeyrunner-自动化测试工具

    这篇博客帮助挺大,我补充部分,帮助同样的小白哈哈,侵删 https://www.cnblogs.com/lynn-li/p/5885001.html 1.前期准备 需要安装:JDK,SDK,pytho ...

  7. 远程连接身份验证错误,又找不到加密Oracle修正

    远程连接服务器出现了错误,错误信息为:远程连接身份验证错误,又找不到加密Oracle修正. 服务器系统:Windows Server2016 客户端系统:Windows10家庭版和专业版   出错原因 ...

  8. x宝23大洋包邮的老式大朝华MP3播放器简单评测

    (纯兴趣测评,非广告) 最近逛X宝,看到了这个古董级MP3播放器居然还在售,于是脑抽+情怀泛滥买了一个. 然后呢,从遥远的深圳跨越好几千公里邮过来了这个玩意: 那节南孚5号电池是我自己的,是为了对比一 ...

  9. REST API disable / enable service auto start by API

    how to disable service auto start by API as the following how to enable service auto start by API as ...

  10. CMake简介

    目录 一.CMake简介 二.CMake典型示例 源代码 demo.cpp cmake脚本 CMakeLists.txt 编译流程 三.CMake常用命令 常用命令介绍 设置编译目标类型 指定编译包含 ...