0

I have a dictionary let say

prices={
    "set1": {
        "price1": 200,
        "price2": 300
         ....
    },
    "set2": {
        "price1": 200,
        "price2": 300,
        ....

    }
}

I have a set of rules. let say

rules={
    "set1": {
        "a>0 and b<10 and price1>300": 1.5,
        "a+b=10 or c>20": 2.5
         ....
    },
    "set2": {
        "a=c and b!=d or price2<100": 3.5,
        "a=b and price2 in range(100,300)": 4,
        ....

    }
}

Here a,b,c,d.. are variables defined with some values if any of the rules in the corresponding set satisfies then it will be added to prices in prices.

Right now I am using if eval(rule) in for loops but total around near 20000 loops are there so taking at least 5 minutes to execute.

Is there any option to make it faster?

I am using Django framework for this api

Edit: when I use lambda it is showing an error as variable not defined

Shreya Maria
  • 488
  • 8
  • 18
user3797053
  • 497
  • 1
  • 8
  • 18
  • Possible duplicate of [Python: Way to speed up a repeatedly executed eval statement?](https://stackoverflow.com/questions/12467570/python-way-to-speed-up-a-repeatedly-executed-eval-statement) – DSC Jun 10 '19 at 19:13
  • if i use lambda i am getting error a b c etc not defined – user3797053 Jun 10 '19 at 19:22
  • Check the "sidenote": `eval('lambda v: ' + expression)`, try replacing `v` with `a, b, c`. – Jeppe Jun 10 '19 at 19:28

1 Answers1

1

You should use lambdas or functions for this type of process. To do so, you would need to ensure that the variables used in the lambdas or functions are in scope of the declaration of your rules dictionary. But perhaps this may prove difficult if your code and rule declarations are not contained within a single function.

You have not posted enough contextual information/code for us to determine where your approach is failing, but perhaps an example will put you on the right track:

prices={
    "set1": {
        "price1": 200,
        "price2": 300
    },
    "set2": {
        "price1": 200,
        "price2": 250
    }
}

def Rule(expression,priceValue):
    return (compile(expression,"<string>","eval"),priceValue)

rules={
    "set1": [
        Rule("a>0 and b<10 and price1>300",1.5),
        Rule("a+b==10 or c>20",2.5)
    ],
    "set2": [
        Rule("a==c and b!=d or price2<100",3.5),
        Rule("a==b and price2 in range(100,300)",4)
    ]
}

context = { "a":5, "b":5, "c":5, "d":0 }
for setId,priceSet in prices.items():
    price = []
    for rule,value in rules[setId]:
        context.update(priceSet)
        if eval(rule,globals(),context):
            price.append(value)
    print(setId,price,sum(price))

# set1 [2.5] 2.5
# set2 [3.5, 4] 7.5 
  • To make repeated eval() function calls faster, you can use compile() to obtain a "compiled" version of your string formula. This is what you should store in your rules dictionary.
  • The content of a rule set only needs to be an array of tuples. This will make using the rules list a bit easier.
  • In your sample rule expressions, you are using a single equal sign (=) for testing equality, Python needs a double equal sign otherwise the formulas will not produce the expected results.

I am not sure what you meant by "it will add to prices in prices". Since your rules are already using price1 and price2 in their expressions, they seem to need the content of the prices dictionary (or at least one of its sets) to produce an answer. Why (and how) then would they update the very data structure that they rely upon to respond ? and with what keys ?. I could not produce an example that meets that requirement. Hopefully you will be able to adapt my example to your needs.

Alain T.
  • 40,517
  • 4
  • 31
  • 51