2

I am trying to write a Python script that accepts a string list of criteria, compares it to values in a dictionary, and executes if the criteria are met (or it throws an error message).

An example criteria list and dictionary values are given below:

criteria_list = ['A < 5', 'B == 6', '3 < C < 4']
dict = {'A': 3, 'B': 6, 'C': 3.5, 'D': 5, 'E': 100}

My initial thought was to have load in the values of the dictionary that are relevant to the criteria by checking if the criteria values are in the dictionary. I could then check if the value satisfies the criteria and then execute the code. If the criteria value is not in the dictionary then throw an error (i.e. criteria_list = [..., "Z == 10", ...] > throws an error that Z is not in the dictionary).

This is what I have now, but I'm having trouble finding a way to save the key value locally to use the eval(criteria) to check if the criteria is True or False.

crit_sum = 0
     
for criteria in criteria_list:        
   for key in dict.keys():       
      if (key in criteria.split()):
                 
         #SAVE KEY VALUE (i.e. A = 3)
                 
         if eval(criteria):
            crit_sum += 1
        
         elif (key not in critera.split()):
            print("criteria not in dictionary!")
    
if crit_sum == len(criteria_list):  #execute code if all criteria are TRUE                
   #EXECUTE CODE

Let me know what you think a good way of saving values locally or another way that could work for evaluating an arbitrary sized criteria list.

lmiguelvargasf
  • 63,191
  • 45
  • 217
  • 228
mojojojo
  • 157
  • 1
  • 8
  • Hi mojojojo, I actually created a pypi package that does this called [ifcollector](https://github.com/jgrugru/ifcollector). You can install it with `pip3 install ifcollector`. If you're wanting to accept an arbitrary amount of criteria, pass the criteria to a function and accept *args. That way you can accept as many criteria as you need. – Jeff Gruenbaum Jul 14 '21 at 18:03

2 Answers2

1

I'm assuming you're aware of why not to use eval(). However, if you still wish to do so, you can pass an empty dict to the globals argument and your dict to the locals argument of eval(). For example:

criteria_list = ['A < 5', 'B == 6', '3 < C < 4']
all_vars = {'A': 3, 'B': 6, 'C': 3.5, 'D': 5, 'E': 100}

crit_sum = 0
for crit in criteria_list:
    if eval(crit, {}, all_vars): 
        crit_sum += 1

if crit_sum == len(criteria_list):
    print("Success!")

Or, as using a generator expression:

criteria_list = ['A < 5', 'B == 6', '3 < C < 4']
all_vars = {'A': 3, 'B': 6, 'C': 3.5, 'D': 5, 'E': 100}

if sum(eval(crit, {}, all_vars) for crit in criteria_list) == len(criteria_list):
    print("Success!")

When an expression uses a variable that doesn't exist in all_vars (e.g. X == 100), you get a NameError: name 'X' is not defined that can be caught and handled if you prefer.

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
  • Is there a better way that doesn't include eval() that you recommend? I see in the link they have a set attribute function but I'm not using classes. I'm dealing with 20+ keys in the dictionary and want the flexibility to make the criteria list as large or small as I need to be. – mojojojo Jul 16 '21 at 21:17
0
criteria_list = ['A < 5', 'B == 6', '3 < C < 4']
dic = {'A': 3, 'B': 6, 'C': 3.5, 'D': 5, 'E': 100}
crit_sum = 0
for k,v in dic.items():
    for i in criteria_list:
        if k in i:
            if eval(i.replace(k,str(v))):
                crit_sum+=1
        else:
            print("criteria not in dictionary!")
print(crit_sum)
if crit_sum == len(criteria_list):    #execute code if all criteria are TRUE 
    #EXECUTE CODE
Naaman
  • 62
  • 3