用Python实现简易可拓展的规则引擎

用Python实现简易可拓展的规则引擎

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

![]

简单整理下需求

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

实现

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

在苦苦思索的时候,突然灵光一闪~曾经我用过一个自动装机系统--razor,
它使用一种tag语法来匹配机器并打标签,他的语法是这样的:

    ["or",
     ["=", ["fact", "macaddress"], "de:ea:db:ee:f0:00"]
     ["=", ["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", "参数1", "参数2", ...],["操作符2", "参数1", "参数2", ...]
    ]
    ["and",
        [">", 0 , 0.05],
        [">", 3, 2]
    ]

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

    =, !=, >, <, >=, <=

逻辑运算符:

    and, or, not, in

四则运算:

    +, -, *, /

数据转换:

    int, str, upper, lower

其他特殊操作符:

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

代码

    class RuleParser(object):
        def __init__(self, rule):
            if isinstance(rule, basestring):
                self.rule = json.loads(rule)
            else:
                self.rule = rule
            self.validate(self.rule)

        class Functions(object):

            ALIAS = {
                '=': 'eq',
                '!=': 'neq',
                '>': 'gt',
                '>=': 'gte',
                '<': 'lt',
                '<=': 'lte',
                'and': 'and_',
                'in': 'in_',
                'or': 'or_',
                'not': 'not_',
                'str': 'str_',
                'int': 'int_',
                '+': 'plus',
                '-': 'minus',
                '*': 'multiply',
                '/': 'divide'
            }

            def eq(self, *args):
                return args[0] == args[1]

            def neq(self, *args):
                return args[0] != args[1]

            def in_(self, *args):
                return args[0] in args[1:]

            def gt(self, *args):
                return args[0] > args[1]

            def gte(self, *args):
                return args[0] >= args[1]

            def lt(self, *args):
                return args[0] < args[1]

            def lte(self, *args):
                return args[0] <= args[1]

            def not_(self, *args):
                return not args[0]

            def or_(self, *args):
                return any(args)

            def and_(self, *args):
                return all(args)

            def int_(self, *args):
                return int(args[0])

            def str_(self, *args):
                return unicode(args[0])

            def upper(self, *args):
                return args[0].upper()

            def lower(self, *args):
                return args[0].lower()

            def plus(self, *args):
                return sum(args)

            def minus(self, *args):
                return args[0] - args[1]

            def multiply(self, *args):
                return args[0] * args[1]

            def divide(self, *args):
                return float(args[0]) / float(args[1])

            def abs(self, *args):
                return abs(args[0])
        @staticmethod
        def validate(rule):
            if not isinstance(rule, list):
                raise RuleEvaluationError('Rule must be a list, got {}'.format(type(rule)))
            if len(rule) < 2:
                raise RuleEvaluationError('Must have at least one argument.')

        def _evaluate(self, rule, fns):
            """
            递归执行list内容
            """
            def _recurse_eval(arg):
                if isinstance(arg, list):
                    return self._evaluate(arg, fns)
                else:
                    return arg

            r = map(_recurse_eval, rule)
            r[0] = self.Functions.ALIAS.get(r[0]) or r[0]
            func = getattr(fns, r[0])
            return func(*r[1:])

        def evaluate(self):
            fns = self.Functions()
            ret = self._evaluate(self.rule, fns)
            if not isinstance(ret, bool):
                logger.warn('In common usage, a rule must return a bool value,'
                            'but get {}, please check the rule to ensure it is true' )
            return ret

解析

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

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

结束语

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

时间: 2024-12-05 13:41:19

用Python实现简易可拓展的规则引擎的相关文章

使用Drools规则引擎实现业务逻辑

简介:使用规则引擎可以通过降低实现复杂业务逻辑的组件的复杂性,降低应用程序的维护和可扩展 性成本.这篇更新的文章展示如何使用开源的 Drools规则引擎让 Java 应用程序更适应变化. Drools 项目引入了一个新的本地规则表达式语言和一个 Eclipse 插件,使Drools 比以前更容易使用. 要求施加在当今软件产品上的大多数复杂性是行为和功能方面的,从而导致组件实现具有复杂的业务 逻辑.实现 J2EE 或 J2SE 应用程序中业务逻辑最常见的方法是编写 Java 代码来实现需求文档的规

旗正规则引擎规则编辑指南

看到有人问,旗正规则引擎定位就是规则逻辑实现简易,业务员也可以执行,可是试用的时候,突然发现还是有些凌乱,规则编辑感觉还是有点繁琐啊.那我说,方法还是没对路,接下来,我来献上指南. 规则包开发 通过"开始-->程序-->旗正商业规则定制平台->规则配置器"启动规则配置器.启动后, 关闭欢迎首页, 进入到缺省的开发工作空间. 通过规则配置器的测试步骤包括创建工程.创建规则包.定义对象库.定义规则.发布规则包.测试规则包.创建web页面.web方式测试规则包 以下分别讲述

几款常用规则引擎的简单对比及演示

Ilog JRules 是最有名的商用BRMS:Drools 是最活跃的开源规则引擎:Jess 是Clips的java实现,就如JRuby之于Ruby,是AI系的代表: Visual Rules(旗正规则引擎)国内商业规则引擎品牌. 今天对比了一下这四个颇有代表性的规则引擎的规则语言.其中Ilog和visual rules是商业产品,没有机会实战. 1.一样的If--Then 句式与Rete引擎     四者都邑把原本杂乱不勘的if---else---elseif----else,拆成N条带优先

【java规则引擎】之Drools之Rete算法

一:规则引擎--->规则引擎的核心是Pattern Matcher(模式匹配器).不管是正向推理还是反向推理,首先要解决一个模式匹配的问题. --->对于规则的模式匹配,可以定义为: 一个规则是一组模式的集合.如果事实/假设的状态符合该规则的所有模式,则称为该规则是可满足的. 模式匹配的任务就是将事实/假设的状态与规则库中的规则一一匹配,找到所有可满足的规则. 二:什么是模式匹配 对于模式匹配我们都应该不陌生,我们经常使用的正则表达式就是一种模式匹配. 正则表达式是一种"模式(pat

Java规则引擎与其API应用详解

详解 本文对Java规则引擎与其API(JSR-94)及相关实现做了较详细的介绍,对其体系结构和API应用有较详尽的描述,并指出Java规则引擎,规则语言,JSR-94的相互关系,以及JSR-94的不足之处和展望 本文对Java规则引擎与其API(JSR-94)及相关实现做了较详细的介绍,对其体系结构和API应用有较详尽的描述,并指出Java规则引擎,规则语言,JSR-94的相互关系,以及JSR-94的不足之处和展望 复杂企业级项目的开发以及其中随外部条件不断变化的业务规则(business l

使用WEBLOGIC PORTAL规则引擎中实现动态业务逻辑

web|动态 简介 业务应用的需求总是随着业务环境的变化趋势而不断地改变.决策很少是一成不变的,并且竞争压力要求业务逻辑的设计和实现具有灵活性,以快速地适应不断变化的需求.通常,对业务逻辑的更改必须由开发人员来完成,然后进行多次彻底的测试,而这将是一个很耗时的过程.在应用程序的修改工作完成后,需要将其重新部署到服务器,需要留出预定的停机时间,以防应用程序对用户不可用. 对于这个问题,更好的解决方案是通过应用程序之外的一组规则来实现某些业务决策.这些规则并没有被编译到应用程序中,而是在运行时读取并

Java规则引擎工作原理及其应用

摘 要 Java规则引擎是一种嵌入在Java程序中的组件,它的任务是把当前提交给引擎的Java数据对象与加载在引擎中的业务规则进行测试和比对,激活那些符合当前数据状态下的业务规则,根据业务规则中声明的执行逻辑,触发应用程序中对应的操作. 引言 目前,Java社区推动并发展了一种引人注目的新技术--Java规则引擎(Rule Engine).利用它就可以在应用系统中分离商业决策者的商业决策逻辑和应用开发者的技术决策,并把这些商业决策放在中心数据库或其他统一的地方,让它们能在运行时可以动态地管理和修

抽取非基本验证到规则文件:A2D规则引擎

基本验证与业务验证,基本验证就是始终保持不变的验证规则,可以通过如下硬编码实现: public class Order { [Required] [Range(typeof(decimal), "1", "10000")] public decimal Price { get; set; } [Required] [StringLength(30)] public string Customer { get; set; } [Required(AllowEmptyS

Java规则引擎与其API(JSR-94)

简介:本文对Java规则引擎与其API(JSR-94)及相关实现做了较详细的介绍,对其体系结构和API应用 有较详尽的描述,并指出Java规则引擎,规则语言,JSR-94的相互关系,以及JSR-94的不足之处和展望 复杂企业级项目的开发以及其中随外部条件不断变化的业务规则(business logic),迫切需要分离 商业决策者的商业决策逻辑和应用开发者的技术决策,并把这些商业决策放在中心数据库或其他统一的地 方,让它们能在运行时(即商务时间)可以动态地管理和修改从而提供软件系统的柔性和适应性.