Python exec eval
exec函数
- exec() 函数用于 Python 程序的动态执行,可以是字符串,也可以是目标代码。
- 如果它是一个字符串,则该字符串被解析为一组 Python 语句,然后执行这些语句, 除非出现语法错误
- 如果它是一个目标代码,则简单地执行它,如使用compile语句进行编译的语句。
- 无返回值。
exec函数语法
exec(object[, globals[, locals]])
object:如前所述,这可以是字符串或对象代码
globals:可以是字典,参数可选
locals:这可以是一个映射对象,也是可选的
exec函数举例
code_str = """
print("Hello, world")
"""
code_obj = compile(code_str, '<string>', 'exec')
print(code_obj)
print(exec(code_obj)) # 执行对象代码,无返回值
print("*"*20) # 执行
print(exec("a=5+8")) # 执行字符串,无返回值
print(a)
result:
<code object <module> at 0x000002367D9C4930, file "<string>", line 2>
Hello, world
None
********************
None
13
exec支持的函数
- 在 exec()函数中使用任何方法之前,必须明确当前条件下,exec()支持的所有函数,否则就会报错。
# math functions
from math import *
exec("print(dir())")
result:
['June', '__builtins__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'a', 'acos', 'acosh',
'asin', 'asinh', 'atan', 'atan2', 'atanh', 'b', 'ceil',
'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc',
'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp',
'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite',
'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p',
'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder',
's', 'sin', 'sinh', 'sqrt', 'sys', 'tan', 'tanh', 'tau', 'trunc']
globals参数和locals参数
- globals参数是字典类型的。
- locals参数是映射对象,比如可以是字典,集合等。
先来看个例子。
# dir()函数不带参数时,返回当前范围内的变量、方法和定义的类型列表。
# 在第一个print(dir())语句,可以看到当前范围内并无max这个变量,而在第二个print(dir())语句,多了max这个变量,且其为int类型。
list1 = [1, 2, 3, 4, 8]
print(max(list1))
print(dir())
print(max)
max = 2
print(type(max), max)
print(dir())
result:
8
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'list1']
<built-in function max>
<class 'int'> 2
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'list1', 'max']
那么后面如果再调用max函数的话,就会报错,因为在当前范围之内,max已经不是个函数了,它是个变量。
list1 = [1, 2, 3, 4, 8]
print(max(list1))
max = 2
exec("c = max(list1)")
result:
TypeError: 'int' object is not callable
那么就没办法使用max函数了吗?答案是否定的,max函数是内置函数,它并不在当前的范围中。
在下例中,我们使用了globals参数,未使用locals,那么exec执行的范围会被现定于s_globals的。
如果同时指定了locals,那么exec的执行范围会从locals先开始,然后到globals。
# 在s_globals中,存在max函数,那么会执行,同时,在执行完c = max(list1)之后,c也被加入到s_globals之中。而外界是无法访问c的,因为c不在外界的范围。
list1 = [1, 2, 3, 4, 8]
print("max of list1 is:", max(list1))
max = 2
s_globals = {"list1": [1, 2, 3, 4, 8]}
exec("c = max(list1)", s_globals)
print(s_globals["c"])
print(c)
result:
max of list1 is: 8
8
NameError: name 'c' is not defined
再看一个例子
# 优先使用s_locals空间的值,而s_locals中只有a,那么b就从s_globals 中找。
# c的结果存于s_locals空间。
a = 1
b = 2
s_globals = {"a": 100, "b": 200}
s_locals = {"a": 20}
exec("c=a+b", s_globals, s_locals)
print("*"*20)
print(s_locals["c"])
print("-"*20)
print(s_globals["c"])
result:
********************
220
--------------------
print(s_globals["c"])
KeyError: 'c'
eval()函数
- 将字符串str当成有效的表达式来求值并返回计算结果。
- 其它同exec。
a = "max"
b = eval(a + "(1,2,3,10,7)")
print(b)
result:
10
exec与eval函数的区别
- eval()函数只能计算单个表达式的值,而exec()函数可以动态运行代码段。
- eval()函数可以有返回值,而exec()函数返回值永远为None。
# eval中无法执行赋值语句。
a = 5
exec("b=3")
print("exec func", b)
eval("c = 5")
result:
exec func 3
c = 5
^
SyntaxError: invalid syntax
"""
# eval无法执行这么复杂的代码。
data.txt中的内容如下:
def fact(n):
if n==1:
return 1
else:
return n*fact(n-1)
t = fact(6)
print(t)
"""
with open("data.txt", "r") as fp:
msg = fp.read()
exec(msg)
使用注意事项
避免使用eval函数。
主要出于安全考虑,对于不可信的数据源,eval 函数很可能会招来代码注入的问题。
# 例如如下代码是暴露当前目录。隐私遭到了暴露。
eval("__import__('os').system('whoami')")
result:
job\day10
耗费电脑资源
eval("2 ** 888888888", {"__builtins__":None}, {})
这还是轻微的,如果恶意破坏,可以更改你的文件目录,破坏你的文件内容。
那么如何去避免呢?
避免方法
指定 globals 为 {‘builtins’: None}
- 指定 globals 为 {‘builtins’: None},默认的__builtins__包含很多内容,如果将其映射为None,则不会使用默认__builtins__的内容。
- 只能避免一小部分。
将它__builtins__映射成 None,就意味着限定了 eval 可用的内置命名空间为 None,从而限制了表达式调用内置模块或属性的能力。
s_globals = {'__builtins__': None}
eval("__import__('os').system('whoami')", s_globals)
result:
eval("__import__('os').system('whoami')", s_globals)
File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable