顾乔芝士网

持续更新的前后端开发技术栈

Python 内置函数 exec、eval 与 compile的用途



这三个函数都与 动态执行代码(即在运行时把字符串或字节码当作 Python 代码来运行)有关,但它们的定位、接受的输入以及返回值各不相同。下面分别介绍它们的用途、特点、常见用法以及注意事项。

1. exec

用途

  • 执行完整的 Python 语句或语句块(包括赋值、函数定义、类定义、循环、控制流等)。
  • 适用于需要在运行时动态生成并运行 多行复杂结构 的代码。

特点

特性

描述

接受对象

exec(object, globals=None, locals=None)object 必须是 字符串字节码对象(由 compile(..., mode='exec') 生成)或 AST(抽象语法树)。

返回值

永远返回 None。执行的结果只能通过修改传入的作用域 (globals/locals) 来观察。

模式

mode='exec'(对应 compile 的 exec 模式),表示代码是一个完整的语句块,可包含多行、定义等。

作用域控制

可以显式提供 globals 与 locals dict,决定代码在何处执行;若省略,则使用当前局部/全局环境。

安全性

同 eval,如果执行不可信的代码会导致任意代码注入风险(文件读写、网络请求等)。必须严格过滤或限制执行环境。

示例

code = """
def hello(name):
print(f'Hello, {name}!')
x = 10
"""
exec(code) # 定义函数和变量在当前全局作用域
hello('World') # 调用刚才定义的函数
print(x) # 输出 10

使用自定义作用域:

globals_dict = {}
locals_dict = {}
exec("a = 5", globals_dict, locals_dict)
print(globals_dict['a']) # 5

2. eval

用途

  • 求值(evaluate)一个单一的 Python 表达式,返回该表达式的计算结果。
  • 常用于把字符串形式的数学/逻辑表达式转成实际数值或对象。

特点

特性

描述

接受对象

eval(expression, globals=None, locals=None)expression 必须是 字符串字节码对象(由 compile(..., mode='eval'))或 AST

返回值

返回表达式求值后的 结果对象(任意 Python 对象)。

模式

mode='eval'(对应 compile 的 eval 模式),只能是 单个表达式,不允许语句块、赋值或定义。

作用域控制

同 exec,可提供自定义 globals/locals,决定变量查找范围。

安全性

极易被利用进行代码注入;如果要处理用户输入,需要限制可用的名字(例如通过 {'__builtins__': None})或使用专门的解析库(如 ast.literal_eval)。

示例

result = eval("2 + 3 * (4 - 1)")
print(result) # 11
# 使用自定义环境
env = {'x': 10, 'y': 5}
print(eval("x * y", env)) # 50

安全示例(仅允许字面量):

import ast
value = ast.literal_eval("[1, 2, 3]")
# literal_eval 能解析列表、元组、dict、数字、字符串等,但不会执行函数调用或属性访问。

3. compile

用途

  • 把源代码(字符串/AST)编译成可执行的字节码对象,随后可以交给 exec 或 eval 执行,或者直接使用 codeobj.co_code 等低层属性。
  • 常用于在需要多次执行同一段动态生成的代码时,提高效率(一次编译,多次运行)。

特点

特性

描述

接受对象

compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)source 可以是 字符串bytesAST

参数 mode

'exec' → 语句块(用于 exec)'eval' → 单一表达式(用于 eval)'single' → 交互式单行代码(返回的对象可直接 exec,会打印结果)。

返回值

返回一个 code object (types.CodeType)。该对象可以被 exec()、eval() 或 function.__code__ 等使用。

文件名

filename 用于错误追踪(如 traceback 中显示的文件名),通常设为 <string> 或自定义标识。

优化/flags

可传递编译标志 (ast.PyCF_ONLY_AST、Py_OptimizeFlag) 以及优化级别 (optimize=0/1/2) 控制字节码生成细节。

安全性

编译本身不执行代码,但仍会解析并可能产生语法错误;若后续使用 exec/eval,同样需要安全审查。

示例

src = "a = 5\nb = a * 2"
codeobj = compile(src, '<dynamic>', 'exec')
exec(codeobj) # 在当前作用域定义 a、b
print(a, b) # 5 10
expr = "x**2 + y"
code_expr = compile(expr, '<expr>', 'eval')
result = eval(code_expr, {'x':3, 'y':4})
print(result) # 13

使用 single 模式(类似交互式 REPL):

c = compile("1+2", "<repl>", "single")
exec(c) # 会打印 3 (因为单行表达式的结果会自动输出)

综合对比

函数

能处理的代码类型

返回值

常用场景

exec

多行语句块(包括定义、控制流)

None

动态生成函数/类、执行脚本片段

eval

单一表达式

表达式结果对象

计算用户输入的数学公式、解析配置表达式

compile

任意代码(取决于 mode) → 编译为字节码

code object

重复执行同一段动态代码,或在安全审查后再执行

安全注意事项

  1. 绝不直接对未信任的字符串使用 exec/eval。攻击者可以植入恶意代码(文件操作、网络请求、系统调用等)。
  2. 若必须处理用户输入:
  • 使用 限制作用域:globals={'__builtins__': None} 或仅提供必要函数/变量。对表达式使用 ast.literal_eval(只允许字面量)或自行构建安全的解析器(如 sympy、asteval)。
  1. 在生产环境中,最好把动态代码 预编译 并进行 语法检查(比如通过 compile(..., mode='exec') 捕获 SyntaxError),再在受限环境执行。

小结

  • exec → 运行完整的代码块;无返回值,修改作用域。
  • eval → 求值单个表达式;返回计算结果。
  • compile → 把源码转为字节码对象,可供 exec/eval 重复使用,提高效率并提供更细粒度的控制。

掌握这三者的区别与正确使用场景,能够让你在需要动态代码生成、脚本插件系统或交互式 REPL 时写出安全且高效的 Python 程序。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言