📜  Python中的求值函数 eval

📅  最后修改于: 2020-04-10 01:12:22             🧑  作者: Mango

本文讨论了Python中的内置函数eval
这是Python中的一个有趣的实用工具,它允许Python程序在其内部运行Python代码。
所述的eval()方法解析传递给它的Python表达式(代码)。
eval的语法为:

eval(expression, globals=None, locals=None)
  • expression表达式:此字符串被解析并作为Python表达式求值
  • globals(可选):用于指定可用全局方法和变量的字典。
  • locals(可选):另一个字典,用于指定可用的本地方法和变量。

让我们借助一个简单的Python程序进行探索:

from math import *
def secret_function():
    return "密钥是1234"
def function_creator():
    # 要评估的表达式
    expr = raw_input("输入函数(以x表示):")
    # 表达式中使用的变量
    x = int(raw_input("输入x的值:"))
    # 评价表达
    y = eval(expr)
    # 打印评估结果
    print("y = {}".format(y))
if __name__ == "__main__":
    function_creator()

function_creator是评估用户创建的数学函数的函数。
考虑一个输出:

输入函数(以x表示):x*(x+1)*(x+2)
输入x的值:3
y = 60

让我们分析一下代码:

  • 上面的函数将变量x中的任何表达式作为输入。
  • 然后,用户必须输入x的值。
  • 最后,我们通过传递expr作为参数,使用eval()内置函数评估Python表达式。

评估漏洞

我们当前的function_creator版本具有一些漏洞。
用户可以轻松地在程序中公开隐藏的值,也可以调用危险函数,因为eval将执行传递给它的任何内容。
例如,如果您这样输入:

输入函数(以x表示):secret_function()
输入x的值:0

您将获得输出:

y = 密钥是1234

另外,考虑在Python程序中导入os模块时的情况。os模块提供了使用操作系统功能的便携式方法,例如:读取或写入文件。单个命令可以删除系统中的所有文件!
当然,在大多数情况下(例如桌面程序),用户不能做超过编写自己的Python脚本的操作,但是在某些应用程序(例如Web应用程序,信息亭计算机)中,这可能会带来风险!
解决方案是将eval限制仅为我们要提供的函数和变量。

使eval安全

eval函数具有显式传递可以访问的函数或变量列表的功能。我们需要以字典的形式将其作为参数传递。
考虑下面的示例:

from math import *
def secret_function():
    return "密钥是1234"
def function_creator():
    # 要评估的表达式
    expr = raw_input("输入函数(以x表示):")
    # 表达式中使用的变量
    x = int(raw_input("输入x的值:"))
    # 在安全字典中传递变量x
    safe_dict['x'] = x
    # 评价表达
    y = eval(expr, {"__builtins__":None}, safe_dict)
    # 打印评估结果
    print("y = {}".format(y))
if __name__ == "__main__":
    # list of safe methods
    safe_list = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos',
                 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor',
                 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10',
                 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt',
                 'tan', 'tanh']
    # 创建安全方法字典
    safe_dict = dict([(k, locals().get(k, None)) for k in safe_list])
    function_creator()

现在,如果我们尝试运行以上程序,例如:

输入函数(以x表示:secret_function()
输入x的值:0

我们得到输出:

NameError: name 'secret_function' is not defined

让我们逐步分析以上代码:

  • 首先,我们创建一个我们希望允许使用的方法列表,作为safe_list
  • 接下来,我们创建一个安全方法字典。在此字典中,是方法名称,是它们的本地名称空间。
    safe_dict = dict([(k, locals().get(k, None)) for k in safe_list])

    locals()是一个Python内置方法,该方法返回一个字典,该字典将本地范围内的所有方法和变量与其名称空间进行映射。

    safe_dict['x'] = x
  • 在这里,我们也将局部变量x添加到safe_dict。eval函数不会识别x以外的任何局部变量。
  • EVAL接受的词典本地以及全球变量作为自变量。因此,为了确保没有任何内置方法可用于eval表达式,我们还传递了另一个字典以及safe_dict,如下所示:
    y = eval(expr, {"__builtins__":None}, safe_dict)

    因此,通过这种方式,我们使eval求值函数安全无虞!

 

eval的用途

如上文所述,由于安全原因,eval的使用率不高。
尽管如此,在某些情况下它还是很方便的:

  • 您可能希望使用它来允许用户输入自己的“脚本”:小表达式(甚至小函数),可用于自定义复杂系统的行为。
  • eval有时还用于需要评估数学表达式的应用程序中。这比编写表达式解析器容易得多。