脚本专家 发表于 2026-9-8 14:00:00

Python eval与exec函数详解:动态执行表达式及代码示例

在Python编程中,动态类型系统允许开发者在运行时定义和操作变量,而内置函数eval和exec则是实现动态执行代码的两大利器。eval通常用于计算字符串形式的表达式并返回结果,exec则用于执行包含多行语句的代码块但不返回结果。两者都支持自定义全局与局部命名空间,灵活性很高,但同时也伴随着安全风险,尤其是处理不受信任的输入时必须格外谨慎。

一、eval函数的基本用法与示例
eval接收一个字符串表达式,在运行时解析并求值,最后返回计算结果。


expression = "3 + 5"
result = eval(expression)
print(f"表达式 {expression} 的结果是:{result}")


这是一个最简单的演示,eval将字符串"3 + 5"计算为整数8。

eval也可以动态计算包含变量的表达式,变量来自当前作用域:


x = 10
y = 20
expression = "x * y + 5"
result = eval(expression)
print(f"表达式 {expression} 的结果是:{result}")


执行结果为205。eval看到表达式里的x和y时,会从当前全局命名空间中查找对应值。

二、为eval指定命名空间
eval的完整签名是eval(expression, globals=None, locals=None)。开发者可以传入自定义字典来限制表达式可访问的变量,避免使用调用方作用域中的全部变量。


global_vars = {"x": 5, "y": 10}
local_vars = {"z": 2}
expression = "x + y * z"
result = eval(expression, global_vars, local_vars)
print(f"表达式 {expression} 的结果是:{result}")


这里global_vars提供了x和y,local_vars提供了z,最终表达式等价于5 + 10 * 2,输出25。

三、exec函数的基本用法与示例
exec可以执行包含循环、函数定义、类定义等多行语句的Python代码,但不会像eval那样返回计算结果。


code = """
for i in range(3):
    print(f"这是第 {i} 次执行")
"""
exec(code)


执行后会依次输出三次文本。exec不仅可以执行普通语句,还能动态定义函数:


code = """
def dynamic_function(x, y):
    return x + y
"""
exec(code)
result = dynamic_function(5, 3)
print(f"dynamic_function(5, 3) 的结果是:{result}")


exec会将dynamic_function的定义写入当前全局命名空间,所以调用方可以直接调用dynamic_function,最终取得结果8。

四、为exec指定命名空间
exec同样支持globals和locals两个可选参数。当需要把动态代码中产生的变量保存到显式的局部字典中,可以使用下面的写法:


global_vars = {"x": 5, "y": 10}
local_vars = {}
code = """
z = x * y
"""
exec(code, global_vars, local_vars)
print(f"z 的值是:{local_vars['z']}")


代码在执行时从global_vars获取x和y,将计算得到的z写入local_vars。对于这种需要从exec中提取变量的场景,比依赖当前作用域更安全、清晰。

五、eval与exec的安全边界
eval和exec本质上是运行时解释器,可以执行任意传入的字符串代码。如果代码来自用户输入、外部配置文件或网络数据,攻击者可能借此读取系统文件、执行系统命令或破坏运行环境。因此两条原则需要牢记:

1. 不要用eval或exec处理不受信任的输入。
2. 如果确实需要解析表达式,优先考虑使用ast.literal_eval或其他安全解析库。

六、实际应用:基于输入动态计算表达式
一个常见的动态执行示例是命令行计算器。程序循环读取用户输入,交给eval计算并返回结果,同时捕获异常避免程序崩溃:


def calculator(expression):
    try:
      result = eval(expression)
      return result
    except Exception as e:
      return str(e)

while True:
    user_input = input("请输入一个表达式(输入'q'退出):")
    if user_input.lower() == 'q':
      break
    print(f"结果是:{calculator(user_input)}")


这个简易计算器展示了eval在交互式脚本中的价值,但它在生产环境只能用于完全可信的输入,否则存在严重安全漏洞。

七、实际应用:用exec动态修改对象属性
exec还可以根据配置代码批量修改对象属性,适合实现可扩展的配置逻辑:


class MyClass:
    def __init__(self):
      self.attr1 = 1
      self.attr2 = 2

obj = MyClass()
config_code = """
obj.attr1 = 10
obj.attr2 = 20
"""
exec(config_code)
print(f"attr1 的值是:{obj.attr1}")
print(f"attr2 的值是:{obj.attr2}")


最终输出attr1为10,attr2为20。这种方式适合将部分业务逻辑或策略以代码片段形式配置到脚本中,但同样需要限制代码来源。

总结
eval擅长求值表达式并返回结果,exec擅长执行多条语句并可以定义函数、类或修改对象状态。两者都可以通过globals和locals参数控制代码执行环境,从而避免直接污染当前作用域。使用动态执行能力能够显著提升程序的灵活性和可扩展性,但安全始终是前提。对于不信任的字符串,永远不要直接交给eval或exec去执行,否则就是把系统权限交给了攻击者。在充分理解风险并做好隔离的前提下,eval和exec才能成为Python脚本编程中可靠的工具。

热心网友7 发表于 2026-9-8 19:00:00

Re: Python eval与exec函数详解:动态执行表达式及代码示例

感谢楼主分享,讲得很清楚!eval和exec确实是动态执行时最常用的两个工具,特别是自定义globals/locals字典这部分很实用,能有效控制变量作用域。安全提醒也非常到位,不管什么场景,只要输入不可信,就绝对不能碰这两个函数。个人觉得ast.literal_eval作为安全替代方案值得多提一句,在解析一些字面量配置时很管用。期待楼主后续还能补充一些exec在动态生成类或框架路由这类场景的坑,比如怎么处理闭包和变量作用域问题。

热心网友7 发表于 2026-9-8 19:00:00

Re: Python eval与exec函数详解:动态执行表达式及代码示例

感谢楼主整理得这么详细!平时写代码时eval用得少,exec偶尔在加载配置或跑测试的时候用一下,但很多细节确实没你讲得这么清晰。尤其是“指定命名空间”和“通过locals提取变量”这两个点,之前我自己试的时候总是搞混globals和locals的作用,看了你的例子一下就明白了。 另外你最后特别强调了安全边界,这点非常认同。以前见过有人拿eval直接处理前端传过来的表达式,结果被搞出过问题。动态执行虽然灵活,但真的要像你说的那样,只对完全可信的代码开放。要是需要解析点简单的字面量,我也会先用ast.literal_eval挡一道。 对了,我想顺便请教一下:exec在指定globals时如果没带上`__builtins__`,是不是里面访问内建函数会受限?还是说Python会自动补全?这个我之前一直没太搞清楚,希望以后能看到你聊聊这个细节!

热心网友7 发表于 2026-9-8 19:00:00

Re: Python eval与exec函数详解:动态执行表达式及代码示例

楼主的总结很清晰,把 `eval` 和 `exec` 的核心区别、命名空间用法和安全边界都讲明白了。特别是用显式字典来隔离作用域那部分,实际写脚本时很实用,能避免很多命名冲突。 补充一点个人体会:很多初学者容易混淆 `eval` 和 `exec`,其实记住「`eval` 有返回值,`exec` 重执行」基本就够了。另外安全方面,除了 `ast.literal_eval`,如果只是需要解析数字、布尔、None 之类的字面量,`ast.literal_eval` 确实是首选;要支持更复杂但受控的表达式,也可以考虑第三方库,不过非必要还是尽量别直接上 `eval`。 楼主最后那个用 `exec` 按配置片段修改对象属性的例子也很有意思,适合做小范围的策略注入。不过正如你提醒的,代码来源必须严格可信,不然风险确实很大。 感谢分享,收藏了慢慢看。
页: [1]
查看完整版本: Python eval与exec函数详解:动态执行表达式及代码示例