python eval函数基础介绍
Eval函数是python的一个内置函数,作用是将字符串作为有效的Python表达式来求值,并返回计算结果。
Eval的基本语法是 eval(字符串,可选全局命名空间,可选局部命名空间)
我们来举个简单的例子。比如 "3+5" 用引号括起来的话是个字符串。如果直接打印,那就是打印出这个字符串。但是如果加上eval函数,那么就会把这个字符串当成一个python语句来运行,得到的结果就是8。
print("3+5") # 输出:3+5(字符串)
print(eval("3+5")) # 输出:8(表达式计算结果)
当然,eval里的字符串必须是有效的Python表达式,否则会抛出异常。
这个字符串里面还可以包含变量。比如定义个变量 x ,然后在eval里面使用这个变量,这样也可以正常运行。
x = 10
result = eval("x * 5") # 等价于计算 10*5
print(result) # 输出:50
Eval的后面两个参数是命名空间,其中全局命名空间必须是字典类型。局部命名空间可以是字典类型,或者其他映射对象。
当全局命名空间不为空时,就会覆盖其他全局变量,也就是前面代码里的变量就不能使用了。
比如我们指定个空字典作为参数,运行就会因“未定义变量x”出错。但把它设置为 None 值,指定个空的局部命名空间就不会有影响。如果全局和局部命名空间定义了同一个变量的话,会使用局部命名空间的值。比如在这里定义一下 x ,这样使用的就是后面这个 x 的值。
x = 100 # 全局变量
# 全局命名空间为空字典(覆盖原有全局变量x)
eval("x + 5", {}) # 报错:name 'x' is not defined
# 全局命名空间为None(使用原有全局变量x),局部命名空间为空
result = eval("x + 5", None, {}) # 正常运行,结果为105
print(result)
# 局部命名空间覆盖全局变量
result = eval("x + 5", {"x": 20}, {"x": 30}) # 局部x=30,结果为35
print(result)
利用eval函数,我们可以用来动态执行一些简单的代码片段。比如,在简单的配置文件场景中,可以规定一个字典形式的配置文件,获得配置字符串后,利用eval,直接得到每个配置参数的值,无需手动去处理字符串信息。
config_str = "{'width': 800, 'height': 600}"
config = eval(config_str) # 直接将字符串解析为字典
print(config['width']) # 输出:800
再比如,要开发一个简单的计算器,传统方式可能是,让用户输入两个数字和要做的加减等操作,获得输入后在后续代码里再判断这个操作,对两个数字进行处理。
如果用eval,就可以让用户输入整条式子,直接把整条式子转成python表达式得出计算结果。比如用户可以直接输入 1+2*3 ,运行就可以直接得到计算结果了。
user_expr = "1 + 2 * 3"
result = eval(user_expr) # 直接计算表达式
print(result) # 输出:7
正确的使用eval函数可以带来很多便利。但是eval函数最大的风险在于,如果使用不当,可能导致代码注入攻击。例如,如果eval的参数直接来源于用户输入,攻击者可以通过构造恶意字符串,执行任意Python代码,造成严重的安全问题。
就像刚才我们的简单计算器的例子,我们是期望用户会输入数学式子,但是如果是恶意用户,可能就会输入一些破坏性代码。我们这里简单的输入一个打印代码,可以看到打印代码也正常执行了。这样攻击者可以通过构造恶意字符串,对系统造成破坏。
# 危险示例:恶意用户输入可执行任意代码
malicious_input = "import os; os.system('rm -rf /')" # 警告:切勿实际运行!
eval(malicious_input) # 可能导致系统文件被删除
所以使用eval时,一般要避免让用户直接传入,除非进行严格的过滤和验证。还可以通过命名空间来做一些限制。
比如我们定义一个字典,使用 {'__builtins__':{}} ,就可以禁用所有内置函数了。这样输入打印代码或者其他函数代码,就会出错。但是输入简单表达式还是可以正常运行。
# 禁用所有内置函数(包括print)
safe_globals = {"__builtins__": {}}
eval("print('执行')", safe_globals) # 报错:name 'print' is not defined
# 允许的简单计算
result = eval("100 - 50", safe_globals)
print(result) # 输出:50
也可以指定允许运行的函数,比如加上 sum 函数,这样用户就可以输入 sum 函数直接运行了。
allowed_globals = {"__builtins__": {"sum": sum}} # 仅允许sum函数
result = eval("sum([1, 2, 3])", allowed_globals)
print(result) # 输出:6
还有其他库的替代方案,比如如果只是想解析数学表达式,可以用 ast 库的 literal_eval() 方法,这样安全性就更有保障。
import ast
safe_result = ast.literal_eval("10 * (2 + 3)") # 仅允许安全表达式
print(safe_result) # 输出:50
关于eval就介绍到这里,还有什么需要补充的,欢迎在评论区留言探讨。