Using custom variables / functions in eval [Python 3.4.2]?

I have a calculator (python 3.4.2) that can do normal operations using eval.

def calculator(user_input):
    if any(c not in config.valid_cal_chars for c in user_input):
        print("- Invalid Equation | Bad characters")
        return
    elif not any(c in user_input for c in "0123456789"):
        print("- Invalid Equation | No numbers found")
        return
    sys.stdout.write("calculating " + "-".join(gfx.load_sequence))
    time.sleep(0.1)
    print (" | 100%")
    try:
        current_ans = eval(user_input)
    except (SyntaxError, ZeroDivisionError, NameError, TypeError, ValueError):
        print ("- Invalid Equation | Error")
        return
    config.ans = current_ans
    print (current_ans)

      

Here is the config.py that config.ans, config.valid_cal_char refers to:

ans = ("0.0") 
valid_cal_chars = ("0123456789-+/*ansqrt() \n")

      

And if you're wondering what

user_choice

      

refers to a variable, it refers to an input function that I have before this function. This part works, so there is no need to worry about it.

However I am wondering if something like this can be done:

input equation here: 4*4 #this would be saved as the user_input variable
> 16 #the output of the equation
input equation here: sqrt(ans) #this would use the previous answer saved in config.ans (ans) and the sqrt() to find the square root of the previous printed value, so:
> 4

      

Thus, typing ans will give you:

input equation here: 1+1
> 2
input equation here: ans
> 2

      

And using sqrt () will result in:

input equation here: 2+2
> 4
input equation here: sqrt(4)
> 2

      

So, if you don't get it anyway, sqrt () will find the square root of the value you entered. ans uses the previous return value. Thus, combining the two "sqrt (ans)" will give the square root of the previous return value.

When the back margin information is out of the way, I want the user to be able to use them when calculating. While "eval" may not work, I happily use "exec" too (knowing the dangers). However, here's a file called multitool.py (main file) that imports this file (functions.py) to use all the functions I have, including this one.

import os, sys, glob, math, random, login, gfx, config, functions, time

path = "******" #creates path to folder (can be changed by commenting this line out and creating new one)
dirs = os.listdir( path ) #not used currently

functions.load_sequence_complete()

functions.username_login()
time.sleep(0.05)
functions.password_login()
print ("\n[credentials have been verified! proceeding to main program " + "-".join(gfx.load_sequence) + "]\n")
time.sleep(0.1)

program = True
while (program == True):
    user_choice = functions.choice_selecter()
    functions.validate_choice(user_choice)

      

If you need any other information please post it in the comments or answers below so that I can edit this to help you help me :)

+3


source to share


2 answers


You can do it in the first method, you can define it ans

as global at the beginning of the function and then the result eval(user_input)

and use it ans

instead current_ans

everywhere.

What would your function look like if everything works -

def calculator(user_input):
    global ans
    if any(c not in config.valid_cal_chars for c in user_input):
        print("- Invalid Equation | Bad characters")
        return
    sys.stdout.write("calculating " + "-".join(gfx.load_sequence))
    time.sleep(0.1)
    print (" | 100%")
    try:
        ans = eval(user_input, {'ans':ans,'sqrt':sqrt},{})
    except (SyntaxError, ZeroDivisionError, NameError, TypeError, ValueError):
        print ("- Invalid Equation | Error")
        return
    config.ans = ans
    print (ans)

      

Note that I got rid of the part elif

in the function, as it would otherwise not allow input, like - ans

, - you might want to rethink how to rewrite it.

Example / Demo -

>>> def calculator(user_input):
...     global ans
...     if any(c not in "0123456789-+/*ansqrt() \n" for c in user_input):
...         print("- Invalid Equation | Bad characters")
...         return
...     time.sleep(0.1)
...     print (" | 100%")
...     try:
...         ans = eval(user_input, {'ans':ans,'sqrt':sqrt},{})
...     except (SyntaxError, ZeroDivisionError, NameError, TypeError, ValueError):
...         print ("- Invalid Equation | Error")
...         return
...     print (ans)
...
>>>
>>> calculator('1+2')
 | 100%
3
>>> calculator('ans')
 | 100%
3
>>> from math import sqrt
>>> calculator('sqrt(ans)')
 | 100%
1.7320508075688772

      




Although you can also use eval

like -

ans = eval(user_input, {'ans':ans,'sqrt':sqrt},{})

      

This would restrict eval to accept only ans

and sqrt

as these names.

But you should reconsider the usage eval()

, because even after limiting global and local users in it, users can still do harm. What for? Check here.

+2


source


Eval is a bad idea here for a number of reasons. Although you can easily do what you described by calling:

eval(user_input, {'ans': config.ans})

      



you should investigate correct expression parsing. Something like a module pyparsing

should help you here. You can find an example calculator for this project here: http://pyparsing.wikispaces.com/file/view/fourFn.py/30154950/fourFn.py

0


source







All Articles