Dynamic expression in Python [closed]

I will get condition from json file where the condition is '<=' and need to compare two columns containing dates.

So I tried as below:

                left = y['predicate']['left']
                right = y['predicate']['right']
                grp1 = y['groupby']['groupByFields']
                grp2 = y['groupby']['aggregateField']
                ope = y['groupby']['aggregateOperation']
                res = y['groupby']['result']
                logic = y['predicate']['logic']
                result[left] = pd.to_datetime(result[left])
                result[right] = pd.to_datetime(result[right])
                res = result.loc[result[left] + eval(logic) + result[right]]

By using the following code it is working perfectly

res = result.loc[result[left] <= result[right]]

and i tried these also

res = result.loc[eval(str(result[left]) +" " + logic +" " + str(result[right]))]

                  

I am getting error.


Solution 1:

Please do not use eval. It is very dangerous - almost always more dangerous than you expect - and there is rarely a good reason to use it. The current problem is definitely not a good reason.

Instead, simply use the logic string to look up a function which can do the comparison. The standard library module operator provides such functions:

import operator

ops = {
    '==': operator.eq, '!=': operator.ne,
    '<=': operator.le, '>=': operator.ge,
    '<': operator.lt, '>': operator.gt
}

# later:
res = result.loc[ops[logic](result[left], result[right])]

# Error handling is left as an exercise.

You can also create such named functions yourself:

def eq(x, y):
    return x == y

Or use lambda: ops = {'eq': (lambda x, y: x == y), ...

In all cases, the lookup should contain the functions themselves, not the result of a call.

Solution 2:

This is because str(result[left]) and str(result[right]) cause these dataframe columns to be printed, while you want them to become result[left] {logic} result[right].

logic = ">=" # For example
res = result.loc[eval(f"result[left] {logic} result[right]")]

using f-string formatting (Python 3.6+)

Note: Use eval are your own risk. I would advise using other methods. There's other comments that outline some.