跳到主要内容

Python eval() 函数

eval() 方法解析传递给它的表达式,并在程序中运行 Python 表达式(代码)。

示例

number = 9

# eval 执行作为参数传递的乘法
square_number = eval('number * number')
print(square_number)

# 输出:81

eval() 语法

eval() 的语法是:

eval() 参数

eval() 函数接受三个参数:

  • expression - 被解析并评估为 Python 表达式的字符串
  • globals(可选)- 一个字典
  • locals(可选)- 一个映射对象。字典是 Python 中标准和常用的映射类型。

本文稍后将讨论 globals 和 locals 的使用。

eval() 返回值

eval() 方法返回从表达式中评估的结果。

示例 1:Python 中 eval() 的工作方式

x = 1
print(eval('x + 1'))

输出

2

这里,eval() 函数评估表达式 x + 1 并使用 print 显示这个值。

示例 2:演示 eval() 使用的实用示例

# 计算正方形周长
def calculatePerimeter(l):
return 4*l

# 计算正方形面积
def calculateArea(l):
return l*l

expression = input("输入一个函数:")

for l in range(1, 5):
if (expression == 'calculatePerimeter(l)'):
print("如果长度为", l, ", 周长 = ", eval(expression))
elif (expression == 'calculateArea(l)'):
print("如果长度为", l, ", 面积 = ", eval(expression))
else:
print('错误的函数')
break

输出

输入一个函数:calculateArea(l)
如果长度为 1 , 面积 = 1
如果长度为 2 , 面积 = 4
如果长度为 3 , 面积 = 9
如果长度为 4 , 面积 = 16

使用 eval() 时的警告

设想一种情况,您正在使用 Unix 系统(macOS、Linux 等),并且您已导入了 os 模块。os 模块提供了一种便携式使用操作系统功能的方法,比如读取或写入文件。

如果您允许用户使用 eval(input()) 输入值,用户可能会发出命令来更改文件,甚至使用命令:os.system('rm -rf *') 删除所有文件。

如果您在代码中使用 eval(input()),最好检查用户可以使用哪些变量和方法。您可以使用 dir() 方法 查看可用的变量和方法。

from math import *
print(eval('dir()'))

输出

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'os', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

在 eval() 中限制可用方法和变量的使用

更多时候,并不需要 eval() 表达式(传递给 eval() 的第一个参数)中使用的所有可用方法和变量,甚至可能存在安全漏洞。您可能需要限制这些方法和变量的使用。您可以通过向 eval() 函数传递可选的 globals 和 locals 参数(字典)来实现。

1. 同时省略 globals 和 locals 参数

如果两个参数都省略(如在我们之前的示例中),表达式将在当前范围内执行。您可以使用以下代码检查可用的变量和方法:

print(eval('dir()')

2. 传递 globals 参数;省略 locals 参数

globals 和 locals 参数(字典)分别用于全局和局部变量。如果省略 locals 字典,默认为 globals 字典。意味着 globals 将用于全局和局部变量。

注意: 您可以使用 Python 中的 globals()locals() 内置方法检查当前的全局和局部字典。

示例 3:传递空字典作为 globals 参数

from math import *
print(eval('dir()', {}))

# 代码将引发异常
print(eval('sqrt(25)', {}))

输出

['__builtins__']
Traceback (most recent call last):
File "<string>", line 5, in <module>
print(eval('sqrt(25)', {}))
File "<string>", line 1, in <module>
NameError: name 'sqrt' 未定义

如果您传递一个空字典作为 globals,只有 __builtins__ 可用于 expression(传递给 eval() 的第一个参数)。

即使我们在上面的程序中导入了 math 模块,表达式也无法访问 math 模块 提供的任何函数。

示例 4:使特定方法可用

from math import *
print(eval('dir()', {'sqrt': sqrt, 'pow': pow}))

输出

['__builtins__', 'pow', 'sqrt']

这里,表达式只能使用 sqrt()pow() 方法以及 __builtins__

也可以根据您的愿望更改表达式可用方法的名称:

from math import *
names = {'square_root': sqrt, 'power': pow}
print(eval('dir()', names))

# 使用 square_root 表达式
print(eval('square_root(9)', names))

输出

['__builtins__', 'power', 'square_root']
3.0

在上面的程序中,square_root() 使用 sqrt() 计算平方根。然而,直接尝试使用 sqrt() 会引发错误。

示例 5:限制对 built-ins 的使用

您可以如下限制表达式中对 __builtins__ 的使用:

eval(expression, {'__builtins__': None})

3. 同时传递 globals 和 locals 字典

您可以通过传递 locals 字典使所需的函数和变量可用。例如:

from math import *

a = 169
print(eval('sqrt(a)', {'__builtins__': None}, {'a': a, 'sqrt': sqrt}))

输出

13.0

在这个程序中,表达式只能使用 sqrt() 方法和变量 a。所有其他方法和变量都不可用。

通过传递 globals 和 locals 字典限制 eval() 的使用将使您的代码安全,特别是当您使用用户提供给 eval() 方法的输入时。

注意: 有时,即使是限制了名称,eval() 也不安全。当一个对象及其方法变得可访问时,几乎可以做任何事情。唯一安全的方法是验证用户输入。