在eval中使用用户定义的变量/函数 [Python 3.4.2]?

3

我有一个计算器(Python 3.4.2),可以使用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)

这里是config.py,config.ans和config.valid_cal_char都在引用它:
ans = ("0.0") 
valid_cal_chars = ("0123456789-+/*ansqrt() \n")

如果您想知道什么是
user_choice

变量是指我在此函数之前拥有的输入函数。那部分已经起作用,所以不用担心。

但是,我想知道是否可能做到这样:

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

打“ans”将会得到:

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

使用sqrt()将会得到:

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

如果你还不明白,sqrt() 函数会找到输入值的平方根。ans 变量则是使用先前返回的值。因此,将这两个组合在一起 "sqrt(ans)" 将给出先前返回值的平方根。

背景知识介绍完毕,我想让用户在计算时使用这些函数。虽然 "eval" 可能不起作用,但我很乐意使用 "exec"(了解其中的危险)。然而,这是一个 multitool.py 的主文件,它导入了这个文件 (functions.py) 来使用我在里面所有的函数,包括这个。

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)

如果您需要其他信息,请在评论或答案中提出,这样我就可以编辑此内容以帮助您帮助我 :)
2个回答

3
你可以使用第一种方法,可以在函数开头将ans定义为全局变量,然后将eval(user_input)的结果赋值给它,并且在所有地方使用ans代替current_ans
假设一切正常,你的函数应该如下所示 -
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)

请注意,我在函数中删除了elif部分,否则它将不允许像ans这样的输入通过 - 您可以重新考虑如何重写它。
示例/演示 -
>>> 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

虽然你也可以使用eval,如下所示 -

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

这将限制 eval 函数只能使用 anssqrt 作为名称。

但是,您仍应重新考虑使用 eval(),因为即使在其中限制了全局和本地变量,用户仍然可能造成伤害。为什么?在此处查看。


0

在这里使用Eval是一个不好的想法,原因有很多。虽然你可以通过调用以下内容轻松完成所描述的操作:

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

你应该研究正确的表达式解析方法。像pyparsing模块这样的工具可以帮助你。你可以在这个项目中找到一个示例计算器:https://github.com/pyparsing/pyparsing/blob/master/examples/fourFn.py


谢谢!我会去查看pyparsing模块,但是这里的sqrt()函数是如何工作的呢? - PhoenixFieryn
Pyparsing 不再托管在 wikispaces.com 上。请前往 https://github.com/pyparsing/pyparsing。 - PaulMcG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接