动态编译指的是在运行时接收字符串,动态的将其编译为python可执行的代码的功能.
python提供了两个函数用于实现动态编译:
exec
和eval
函数
-
eval(exp[, globals[, locals]])
globals
是字典形式,表示全局命名空间,如果传入globals
的字典中缺少__builtins__
的时候,当前的全局命名空间将作为globals
参数输入并在表达式计算之前被解析.locals
则为任何映射对象,表示局部命名空间,与globals
两者默认相同.
如果两者都省略则表示在eval的调用环境中执行
-
exec()
与
eval()
类似的是exec()
方法,但exec
是翻译并执行.exec
常与文件读取操作结合使用,直接传递python的代码文件运行
a = eval("lambda *x: sum(x)")
a(1,2,3,4,5)
15
%timeit a(1,2,3,4,5,6,7,8,9)
258 ns ± 2.81 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit lambda *x:sum(x)(1,2,3,4,5,6,7,8,9)
58 ns ± 0.763 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
exec("aa = lambda x: x")
aa(10)
10
eval
和exec
有两个弊端:
-
降低运算效率
如上面看到的,运行时间上差距不小
-
安全性
这主要是因为可以调用一些危险的方法而没有设限.也就是所谓的代码注入攻击.
当然了,我们也可以通过限制globals
和locals
来实现对可用项的限制.
如果只是为了传入参数,那么可以使用ast
库的literal_eval
函数,它是安全的
import ast
ast.literal_eval("[1,2,3]")
[1, 2, 3]
在Python中做元编程时,最好不用exec
和eval
函数.如果接收的字符串(或片段)来自不可信的源,那么这两个函数会带来严重的安全风险.Python提供了充足的内省工具,大多数时候都不需要使用exec
和eval
函数.然而,Python核心开发者实现namedtuple
函数时选择了使用exec
函数,这样做是为了让生成的类代码能通过._source
获取.