Python compile/eval/exec:动态代码执行的安全与性能
请解释Python中compile()、eval()和exec()三个内置函数的区别和使用场景。说明compile()的三个模式(exec/eval/single)以及eval在表达式求值中的局限。如何安全使用这些动态执行函数(限制命名空间、ast.literal_eval、沙箱化)?给出一个允许用户自定义过滤条件的实际案例。
回答
我还是少年
三者的区别:
| 函数 | 用途 | 可执行语句? | 返回值 |
|------|------|-------------|--------|
| eval(expr) | 计算单个表达式 | ❌ | 表达式结果 |
| exec(code) | 执行语句块 | ✅ | None |
| compile(src, file, mode) | 编译为code object | ✅ | code object |
# eval:只能表达式
eval('2 + 3 * 4') # 14
eval('[x**2 for x in range(5)]') # [0,1,4,9,16]
# exec:可任意语句
exec('a = 1\nb = a + 2\nprint(b)') # 3
# compile:预编译复用
code_obj = compile('x + y * 2', '<expr>', 'eval')
result = eval(code_obj, {'x': 5, 'y': 3}) # 11
安全使用:
# 限制命名空间(白名单)
safe_globals = {'__builtins__': {'max': max, 'min': min, 'abs': abs, 'len': len}}
safe_locals = {'data': [1, 2, 3]}
try:
result = eval('max(data)', safe_globals, safe_locals)
except Exception as e:
result = None
# ast.literal_eval(最安全,仅求值字面量)
import ast
ast.literal_eval('[1, 2, 3]') # [1, 2, 3] 安全
ast.literal_eval('__import__("os")') # ValueError!
用户自定义过滤条件示例:
def apply_filter(data, condition):
"""condition如 'age > 18 and city == "Beijing"' """
safe_builtins = {'True': True, 'False': False, 'None': None}
try:
code = compile(condition, '<filter>', 'eval')
return [item for item in data if eval(code, {'__builtins__': {}}, {**safe_builtins, **item})]
except:
return data
注意:即使限制__builtins__,eval/exec仍可能被绕过(如().__class__链)。生产环境建议使用受限的DSL或restrictedpython库。