Python exec 内置语句(完整指南)

Python exec 内置语句:动态执行代码的强大工具

在 Python 的众多内置函数中,exec 可能是最容易被误解、也最容易被滥用的一个。它不像 print 那样直观,也不像 len 那样日常高频使用。但一旦掌握,它能让你的程序具备“自我修改”“动态生成代码”甚至“运行用户输入脚本”的能力。

你有没有想过,一段字符串内容,可以像程序一样被“执行”?比如:

code = "print('Hello, 动态世界!')"
exec(code)

这段代码会输出 Hello, 动态世界!。没错,exec 就是让 Python 把字符串当作代码来运行的“执行器”。

今天我们就来深入聊聊这个强大的内置语句——Python exec 内置语句,从基础用法到实战技巧,再到安全风险,一步步带你掌握它的真谛。


exec 的基本语法与运行机制

exec 的语法非常简单:

exec(object, globals=None, locals=None)
  • object:必须是字符串或代码对象,包含要执行的 Python 代码。
  • globals:全局命名空间,可选参数。
  • locals:局部命名空间,可选参数。

想象一下,exec 就像一个“代码翻译官”——它接收一段“文字版”代码(字符串),然后翻译成机器能理解的指令,再执行。

示例:最简单的 exec 使用

code_str = "x = 10\ny = 20\nprint(x + y)"

exec(code_str)

输出结果:

30

💡 中文注释

  • code_str 是一个包含多行代码的字符串,其中 x = 10y = 20 是赋值语句,print(x + y) 是输出语句。
  • exec 会把这段字符串当作完整的 Python 脚本运行,因此变量 xy 被成功创建并计算出结果。
  • 注意:exec 并不会返回任何值,它只是“执行”动作,不产生返回值。

作用域与命名空间:exec 的“舞台”和“演员”

exec 的运行依赖于两个关键概念:全局命名空间(globals)和局部命名空间(locals)。它们决定了代码执行时变量的可见范围。

作用域示例:全局 vs 局部

global_var = "我是全局变量"

code = """
print(global_var)        # 可以访问全局变量
local_var = "我是局部变量"
print(local_var)
"""

exec(code)

💡 中文注释

  • global_varexec 外定义,所以 exec 内部可以访问它。
  • local_var 是在 exec 内部创建的,执行结束后就“退出舞台”了,外部无法访问。
  • 这说明 exec 的变量作用域是“临时的”——只在代码块内有效。

使用命名空间控制变量范围

my_globals = {}

my_locals = {}

exec("x = 100; y = 200", my_globals, my_locals)

print(my_globals)  # {'__builtins__': <module '__builtin__' (built-in)>}
print(my_locals)   # {'x': 100, 'y': 200}

💡 中文注释

  • my_globalsmy_locals 是两个独立的字典,用来存放变量。
  • exec 执行时,变量被存入这两个字典中,不会污染当前环境。
  • 这是安全使用 exec 的关键:隔离作用域,避免意外变量冲突。

动态代码生成:让程序“自己写代码”

exec 最强大的应用场景之一是“动态生成代码”。比如,根据用户输入或配置文件,动态创建函数、类或循环逻辑。

示例:动态创建函数

function_code = """
def greet(name):
    return f'你好,{name}!欢迎使用 Python exec 内置语句'
"""

exec(function_code)

print(greet("小明"))  # 你好,小明!欢迎使用 Python exec 内置语句

💡 中文注释

  • function_code 是一个字符串,定义了一个函数 greet
  • exec 把这个字符串当作代码运行,于是函数就被“创建”了。
  • 之后,你可以像调用普通函数一样调用 greet
  • 这种方式在插件系统、配置驱动程序中非常实用。

实战案例:配置驱动的自动化脚本

假设你有一个任务配置文件(如 JSON 或字符串),你想根据配置动态执行不同逻辑。

案例:根据配置运行不同计算

config = """
operation = input('请输入操作类型(加法/乘法):')
a = float(input('请输入第一个数:'))
b = float(input('请输入第二个数:'))

if operation == '加法':
    result = a + b
elif operation == '乘法':
    result = a * b
else:
    result = '无效操作'

print(f'结果是:{result}')
"""

exec(config)

💡 中文注释

  • 这段代码模拟了一个可配置的任务系统。
  • 用户输入操作类型和数值,exec 会动态执行对应的逻辑。
  • 实际项目中,你可以从文件、数据库或网络读取配置,再用 exec 执行。
  • 注意:这种模式在生产环境中需严格校验输入,防止安全风险。

安全风险与最佳实践:别让 exec 成为“后门”

exec 是一把双刃剑。它强大,但危险。一旦用户输入了恶意代码,后果不堪设想。

危险示例:恶意代码注入

user_input = "import os; os.system('rm -rf /')"

exec(user_input)  # 这会尝试删除系统根目录!

⚠️ 警告
上面的代码虽然在本地可能不会执行(因为权限问题),但一旦在服务器上运行,后果严重。
exec 可以执行任意代码,包括删除文件、上传后门、获取敏感信息等。

安全使用建议

  1. 绝不直接执行用户输入,除非经过严格过滤和沙箱隔离。
  2. 使用 globalslocals 限制作用域,避免访问 ossys 等敏感模块。
  3. 使用 ast 模块代替 exec:如果只是计算表达式,优先用 eval(注意 eval 也有风险)或 ast.literal_eval
  4. 在沙箱环境中运行:使用 restrictedpythonPyPy 的沙箱机制。

安全示例:限制执行环境

safe_globals = {
    '__builtins__': {
        'print': print,
        'len': len,
        'abs': abs,
        'int': int,
        'float': float,
        'str': str,
    }
}

code = "x = 10 + 20; print(x)"

exec(code, safe_globals)  # 安全执行

💡 中文注释

  • safe_globals 中只保留了安全的内置函数,移除了 ossys 等危险模块。
  • 即使用户输入 import os,也会报错,因为 os 不在 __builtins__ 中。
  • 这是保护 exec 安全性的核心手段。

exec 与 eval 的区别:你真的分清了吗?

很多人混淆 execeval。它们都用于执行字符串代码,但用途不同。

特性 exec eval
用途 执行任意 Python 语句(如赋值、循环、函数定义) 只能执行表达式,返回值
返回值 无返回值(返回 None) 返回表达式的结果
示例 exec("x = 10") eval("2 + 3") → 返回 5

对比示例

exec("a = 10; b = 20")
print(a + b)  # 输出 30

result = eval("a + b")  # 报错!a、b 未定义

💡 中文注释

  • exec 能创建变量,eval 不能。
  • eval 适用于数学计算、配置解析等场景,而 exec 适合复杂逻辑动态生成。
  • 如果你只需要计算一个表达式,优先用 eval,更安全。

总结:Python exec 内置语句的正确打开方式

exec 是 Python 中一个非常强大但需谨慎使用的内置语句。它让程序具备了“自我演化”的能力,能动态执行代码,提升灵活性。

但正如所有强大工具一样,它也伴随着风险。我们不能因为它的便利就滥用,尤其是在处理用户输入或外部配置时。

正确使用 exec 的原则是

  • 仅在必要时使用,优先考虑静态代码或配置文件。
  • 始终使用 globalslocals 隔离作用域。
  • 严格限制可用的内置函数,避免导入危险模块。
  • 在生产环境中,尽量用 ast 或配置解析替代 exec

掌握 exec,不是为了“炫技”,而是为了在合适场景下,用对工具解决复杂问题。愿你在使用 Python exec 内置语句时,既能享受它的强大,也能守住安全的底线。