How to write a basic rule engine in Python

While there are many existing rules engines in Python, such as the amazing rule-engine framework, I thought it would be an interesting exercise to utilize ChatGPT to help me write my own basic rule engine from scratch. So, without further ado – I prese…


This content originally appeared on DEV Community and was authored by Scott Rallya

While there are many existing rules engines in Python, such as the amazing rule-engine framework, I thought it would be an interesting exercise to utilize ChatGPT to help me write my own basic rule engine from scratch. So, without further ado - I present to you PYROSE - PYthon Rule-based Operation System Engine.

The basic structure of our rule system is as follows. At the basic foundation of engine we have Facts. Facts are simple objects that represent information we want stored in our system and can contain any kind of information that is relevant to the design of the constraints by which our rules-engine operates. Let us begin by defining a very simple structure for our Facts

from typing import Any

class Fact:
    def __init__(self, **kwargs: Any):
        self.__dict__.update(kwargs)

Here we define a very simple object that can be instantiated with any number of keyword arguments to define our fact. We then update our object used the self.__dict__.update method to add attributes to our object corresponding to the keywords passed to the initializer. We can initialize a fact as follows,

person_fact = Fact(name="John Brown", age="35", occuptation="Software Developer")

Accessing a attribute is as simple using dot notation as if you were access a member or method on the object.

person_fact.age # Returns 35

Now that we have established our Fact object, we need to define a Condition by which to apply our Fact object. Our Conditions are very simple in design as well to start off with. The initializer takes two parameters, a name and an evaluation function. The evaluation function will be applied to a Fact and return a bool. It also contains a method, evaluate, which will take a Fact and return a bool, calling the objects evaluation_function on the Fact. Here is the outline of our Condition class:

from rule_engine.fact import Fact

from typing import Callable, Any, Dict, List

class Condition:
    def __init__(self, name: str, evaluation_function: Callable[[Fact], bool]):
        self.name = name
        self.eval_func = evaluation_function

    def evaluate(self, fact: Fact) -> bool:
        return self.eval_func(fact)

Following a similar suit, we can define our Action class. An Action class takes in a name parameter and a Callable parameter into its initializer. The Callable corresponds to the Action's execution function, what is executed if all the Conditions of the Rule are True. It also contains an execute method which calls the execute function on the given Fact, giving a None type as a result. Here is the definition of the Action class:


from typing import Callable, Any, Dict, List
from rule_engine.fact import Fact

class Action:

    def __init__(self, name: str, execution_function: Callable[[Fact], None]):
        self.name = name
        self.exec_func = execution_function

    def execute(self, fact: Fact) -> None:
        self.exec_func(fact)

So far we've defined our Fact, our Condition, and our Action. We can combine these together to form our Rule class. This will be the main driving force behind our Engine. The Rule class will initially be constructed with a single Action and Condition. Two methods, add_condition and add_action, will allow you to add additional conditions and actions to the Rule as you need.

Finally, a third method, evaluate, will take in a list of Facts. It defines a fact_generator which takes a list of conditions and a list of facts. For each fact, it maps each of the conditions' eval_func against the fact. It then reduces this list to a single boolean value, and if this value is true, we yield the fact.

We then call the fact_generator function, wrap it in a list to get the list of all True facts, and if the length is greater than 0, we iterate through the list of true facts. For each true fact, we iterate through a list of the rules actions and call the action's exec_func on the true fact.

The complete definition of the Rule class is as follows:


from rule_engine.condition import Condition
from rule_engine.action import Action
from rule_engine.fact import Fact

from typing import Any, List
from functools import reduce

from rule_engine.condition import Condition
from rule_engine.action import Action
from rule_engine.fact import Fact

from typing import Any, List
from functools import reduce

class Rule:
    def __init__(self, condition: Condition, action: Action):
        self.conditions = [condition]
        self.actions = [action]

    def add_condition(self, condition: Condition) -> None:
        self.conditions.append(condition)

    def add_action(self, action: Action) -> None:
        self.actions.append(action)

    def evaluate(self, facts: List[Fact]) -> Any:
        def fact_generator(conditions: List[Condition], facts: List[Fact]):
            all_conditions_true = True
            for fact in facts:
                results = map(lambda condition: condition.eval_func(fact), conditions)
                all_conditions_true = reduce(lambda x, y: x and y, results)

                if all_conditions_true:
                    yield fact

        true_facts = list(fact_generator(self.conditions, facts))

        if len(true_facts) > 0:
            for fact in true_facts:
                for action in self.actions:
                    action.exec_func(fact)

You can use the rule engine as follows:


from rule_engine.fact import Fact
from rule_engine.condition import Condition
from rule_engine.action import Action
from rule_engine.rule import Rule

age_cond = Condition(name="Age>=21", evaluation_function=lambda fact: fact.age >= 21)
occupation_cond = Condition(name="Occupation==Software Developer", evaluation_function=lambda fact: fact.occupation == "Software Developer")

print_action = Action(name="Print Fact", execution_function=lambda fact: print("Name: {} Age: {} Occupation: {}".format(fact.name, fact.age, fact.occupation)))

john = Fact(age=25,name="John Brown", occupation="Software Developer")
sarah = Fact(age=35,name="Sarah Purple", occupation="Data Engineer")
barry = Fact(age=27, name="Barry White", occupation="Software Developer")

rule = Rule(condition=age_cond, action=print_action)
rule.add_condition(occupation_cond)

rule.evaluate([john, sarah, barry])

All in all, this is a very basic demo of a very simplistic Rules engine. However, I think it can definitely be built and improved upon in any number of ways, so I hope that you enjoyed reading and using what you learned in your own projects. If you have any questions, comments, suggestions or ideas, please feel free to reach out to me. Thank you for reading.


This content originally appeared on DEV Community and was authored by Scott Rallya


Print Share Comment Cite Upload Translate Updates
APA

Scott Rallya | Sciencx (2023-05-15T01:34:36+00:00) How to write a basic rule engine in Python. Retrieved from https://www.scien.cx/2023/05/15/how-to-write-a-basic-rule-engine-in-python/

MLA
" » How to write a basic rule engine in Python." Scott Rallya | Sciencx - Monday May 15, 2023, https://www.scien.cx/2023/05/15/how-to-write-a-basic-rule-engine-in-python/
HARVARD
Scott Rallya | Sciencx Monday May 15, 2023 » How to write a basic rule engine in Python., viewed ,<https://www.scien.cx/2023/05/15/how-to-write-a-basic-rule-engine-in-python/>
VANCOUVER
Scott Rallya | Sciencx - » How to write a basic rule engine in Python. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2023/05/15/how-to-write-a-basic-rule-engine-in-python/
CHICAGO
" » How to write a basic rule engine in Python." Scott Rallya | Sciencx - Accessed . https://www.scien.cx/2023/05/15/how-to-write-a-basic-rule-engine-in-python/
IEEE
" » How to write a basic rule engine in Python." Scott Rallya | Sciencx [Online]. Available: https://www.scien.cx/2023/05/15/how-to-write-a-basic-rule-engine-in-python/. [Accessed: ]
rf:citation
» How to write a basic rule engine in Python | Scott Rallya | Sciencx | https://www.scien.cx/2023/05/15/how-to-write-a-basic-rule-engine-in-python/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.