how to use Python lists to create dynamic calculations

I am trying to use python to create a lookup table.  The lookup table is in a csv right now.
Category1, .15xPrice + 2.00
Category2, .18xPrice + 2.00

The lookup locates the Category, and the second column is the algorithm that is executed against a given value assigned to the the variable Price.

I do not know how to execute this.

For example:
PRice = 29.95
Category = Category1

THe output would be (29.95 * .15) + 2.00 or 6.4925
How do I accomplish this?

Evan CutlerVolunteer Chief Information OfficerAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Firstly, I suggest to use the standard csv module to read the content of the file. You can do manual split in this case, but using the csv module is not difficult and it is capable to handle more complex input in future.

Secondly, the formula has to be interpreted (that is clear). Python has the eval() function that could be used after fixing the formula syntax; however, it may be dangerous if the formula could contain malicious code. Anyway, it is possible to wrap the calculation to a function to have the calculator abstraction (starting with dangerous eval(), replacing it later by something else -- it can also fix the syntax, or better to say to convert the formula syntax to Python syntax).

Thirdly, you need to map the placeholder inside the formula  (here PRice) the value . Notice also that the 'x' in the formula is difficult to be distinguished from the following Price name technically. So, you have to introduce some rules. The mapping should probably be done through a dictionary as it is easy to insert the valua via a modified placeholder.

Now, the hardwired hack exactly for the case. It can be enhanced later:

import csv

def calc(value, formula):
    print('\n------------------------ calc {}, {!r}'.format(value, formula))
    d = {'Price': value}
    print('Placeholder mapping:', d)
    # Modifying the placeholders in the formula.
    for k in d:
        formula = formula.replace(k, '{' + k + '}')
    print('Fixed placeholders:', repr(formula))
    # Fixing the x operator.
    formula = formula.replace('x', '*')
    print('Fixed operator:', repr(formula))
    # Now insert the values to the formula.
    expression = formula.format_map(d)
    print('Expression:', repr(expression))
    # Do the evaluation and return the result.
    return eval(expression)

if __name__ == '__main__':    
    fname = 'data.csv'
    price = 29.95
    with open(fname, newline='') as f:
        reader = csv.reader(f)
        for row in reader:
            category = row[0]
            result = calc(price, row[1])
            print('{}: {}'.format(category, result))

Open in new window

It prints on my console:

------------------------ calc 29.95, ' .15xPrice + 2.00'
Placeholder mapping: {'Price': 29.95}
Fixed placeholders: ' .15x{Price} + 2.00'
Fixed operator: ' .15*{Price} + 2.00'
Expression: ' .15*29.95 + 2.00'
Category1: 6.4925

------------------------ calc 29.95, ' .18xPrice + 2.00'
Placeholder mapping: {'Price': 29.95}
Fixed placeholders: ' .18x{Price} + 2.00'
Fixed operator: ' .18*{Price} + 2.00'
Expression: ' .18*29.95 + 2.00'
Category2: 7.391

Open in new window

There are some thing not explained (used intentionally for provoking you to ask ;) -- ask here in this question:
if __name__ == '__main__':

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
With the only request to use "*" for multiplication in formulas and not "x":
# read formula file
lookup = { }
for line in open('myFile.cvs', 'r'):
    category, formula = line.strip().split(',')
    lookup[category] = formula
# result is as-if
#      lookup = {'Category1': '.15*Price + 2.00', 'Category2': ' .18*Price + 2.00'}

# given 
Price = 29.95
Category = 'Category1'

# do the math
print eval(lookup[Category])

Open in new window

+1 to Nas-Banov's even simpler (but a bit more dangerous) approach where eval() takes the Price variable directly from the local variables, and for the lookup dictionary filled in advance. However, the file (when open) should always be closed. This is what the with construct does implicitly.

The example with calc() can be rewritten using the approach. It fixes the formulas using another function when loading the lookup table:

import csv

def calc(Price, formula):
    return eval(formula)

def fixed_formula(formula):
    print('\n---------------- fixed_formula({!r})'.format(formula))
    formula2 = formula.strip(). replace('x', '*')
    print('Fixed formula:', repr(formula2))
    return formula2

if __name__ == '__main__':

    # Load the lookup table, fix the formulas.
    fname = 'data.csv'
    lookup = {}
    with open(fname, newline='') as f:
        reader = csv.reader(f)
        for row in reader:
            lookup[row[0]] = fixed_formula(row[1])

    print('=' * 50)
    # Calculate the values for the price and all categories.
    price = 29.95
    for category in lookup:
        result = calc(price, lookup[category])
        print('{}: {}'.format(category, result))

Open in new window

It prints:

---------------- fixed_formula(' .15xPrice + 2.00')
Fixed formula: '.15*Price + 2.00'

---------------- fixed_formula(' .18xPrice + 2.00')
Fixed formula: '.18*Price + 2.00'
Category1: 6.4925
Category2: 7.391

Open in new window

Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

@pepr, i did not see your solution when i wrote mine - there was no answer when i started writing it; i wouldn't have done double-take if i saw yours.

Now, i tried to balance simplicity and correctness for a novice level. But your note about "more dangerous" does not apply:
Even without with, the file will be closed automatically right after the for loop (you can check, files are fine w/o with).
Using eval() sure has dangers but you are using it too and formula.replace() substitution is not making it safer
replacing letter 'x' with '*' will cause havoc if there are formulas like '.23*fee + extra'
@Nas-Banov: No problem with your answer ;) I believe that Evan is novice only concerning Python, not a novice programmer.

The file is closed automatically because the program is terminated, not because it leaves the for loop. Or possibly you may be lucky and the file object is garbage collected. However, it is not a good practice. The with was designed for exactly such situations. How did you check that the file is closed by the for loop?

I did mention the problem with `x` (that it can be a part of an identifier). This is also reason why formulas should be specifically interpreted.

When the substitution of Price in the formula is done, one can add a check that the formula after the substitution does not contain any letters. In such case, the eval is quite safe.
@pepr - i like your idea about checking for remaining letters re securing eval! The file will be closed because its reference count becomes 0 after the loop and then it gets immediately disposed, destructor called etc. Python uses reference counting (plus mark&sweep for cyclic structures). Google it - but let's end discussion here.

As a practical matter, i advise @Evan Cutler to ignore our nit-picking here, just look at out first answers and make use of it.
Evan CutlerVolunteer Chief Information OfficerAuthor Commented:
This was the best discussion ever!!

Thank you so much for your help.  
These solutions worked perfectly and I got my script started.
When I submitted my script to my client, he came at me and told me that the categories might fuzzy match the lookup categories.
This means I have titles that might be part of titles in both places.
But that question I'll give another 500 for.
Thanks guys.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.