CodeWalk

Python compile/eval/exec:动态代码执行的安全与性能

作者:我还是少年 · 2026-05-30 12:55

请解释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库。