Kenstars
Kenstars

Reputation: 660

Alternative to eval in condition check scenarios

I have this json structure

scenarios = [{"outcome_name":"outcome1",
            "checking_condition":[
                      {
                        "db_key":"purchase_date",
                        "operator":">"
                        "db_value":"requested_date"
                         },
                      {
                        "db_key":"purchase_amount",
                        "operator":"<"
                        "db_value":"5000"  
                       }
                      ]
             outcome_selection_criteria:"index0 and index1"
             }]

so if both checking conditions are true I'm marking outcome1 as being the choice selected, there may be any number of outcomes in scenarios list.

Currently while I dynamically check conditions, I'm creating a string of the actual condition parameters like for the purchase amount , the string is :

"4000 < 5000" and I am performing an eval operation on this to get a boolean result. which I'm replacing in the outcome_selection_criteria string for its corresponding index value. then once all the criteria are replaced, i'm performing eval over the string to find the actual result of the outcome.

However this I've heard is slow in terms of processing as eval is ineffecient. I'm not very sure how true this is.

I'm going about this way as it allows to handle dynamically multiple types of conditional situations without changing the actual code I'm building.

is there a better way to go about , rather than using eval.

Additional Code Additions :

while True:

    with open(outcome_name+".json","r") as jsonfile:
         scenarios_data = json.load(jsonfile)

    for each_outcome_dict in scenarios_data:
        outcome_name = each_outcome_dict["outcome_name"]
        for ind, each_condition in enumerate(each_outcome_dict["checking_condition"]):
            key = each_condition["db_key"]
            value = each_condition["db_value"]
            operator = each_condition["operator"]
            true_key = redis_db.get(key,key)
            true_val = redis_db.get(value,value)
            condition = true_key + " " + operator + " " + true_val
            result = str(eval(condition))
            result_index = "index"+ind
            each_outcome_dict["outcome_selection_criteria"] = each_outcome_dict["outcome_selection_criteria"].replace(result_index, result)
        total_outcome = eval(each_outcome_dict["outcome_selection_criteria"])
        if total_outcome:
             outcome_name =  each_outcome_dict["outcome_name"]
             break
    else:
        print "nothing matched"
        break

Upvotes: 2

Views: 153

Answers (1)

chepner
chepner

Reputation: 532053

Define a map of the allowed operator symbols to the function that implements it. Then just look up the desired function when you read the operator from the condition.

In general, the functions exported by operators are more efficient than defining the equivalent function yourself using def or a lambda expression.

import operators

ops = {
  "<": operator.lt,  # lambda x, y: x < y
  ">": operator.gt,  # lambda x, y: x > y
  # ...
}

for outcome in scenarios:
   for cond in outcome["checking_condition"]:
       op = cond["operator"]
       # Whatever foo is, for however you get the actual values to compare
       lhs = foo(cond["db_key"])
       rhs = foo(cond["db_value"])
       result = ops[op](lhs, rhs)
       ...

Upvotes: 3

Related Questions