Python魔法棒`exec()`:让函数“凭空”造变量?新手慎入

360影视 动漫周边 2025-08-15 08:41 2

摘要:很多Python萌新会想到exec这个“魔法”函数。它确实能“变”出变量,但也像一把双刃剑,用不好会伤到自己!今天就来聊聊exec在函数内设置变量的门道和那些你必须知道的“坑”。

想根据用户输入、配置文件或者其他动态条件,在函数里灵活地创建不同名字的变量?

很多Python萌新会想到exec这个“魔法”函数。它确实能“变”出变量,但也像一把双刃剑,用不好会伤到自己!今天就来聊聊exec在函数内设置变量的门道和那些你必须知道的“坑”。

简单说,exec是一个Python内置函数。它的“超能力”是:能把一个字符串当作完整的Python代码来执行! 就像你写在.py文件里的代码一样。

想象一下:你有一个字符串 "x = 11 + 22"。如果你把它丢给exec:

exec("x = 11 + 22")print(x) # 输出 33

神奇吗?x这个变量就被创建并赋值了!它似乎打破了“变量必须先定义后使用”的常规。

核心诉求是:动态变量名!

场景举例1: 你写一个函数process_data(data, prefix),想把处理后的数据存成prefix + "_result"格式的变量。如果prefix="user",你就想创建变量user_result。变量名user_result是运行时根据prefix决定的!场景举例2: 从配置文件读取一批字段名和值,想在函数里直接生成对应的变量。

很多教程(包括网上一些片段)会这样写:

def create_var(value): exec(f"new_var = {value}") # 1. 用exec创建变量 return new_var # 2. 直接返回这个变量result = create_var(100)print(result) # 期待输出100?

结果呢?多半会报错:**NameError: name 'new_var' is not defined**

为什么?这就是exec在函数内最大的“坑”——作用域问题!

1、默认行为: 当exec单独执行一个字符串(不带额外参数)时:

它确实会在当前局部作用域创建变量。但是!这个“局部作用域”是exec自己执行代码时的局部命名空间,并不等同于你函数create_var的局部命名空间!

2、后果:exec执行完毕后,它在自己那个小空间里创建的new_var就消失了。函数主体代码里的return new_var根本找不到这个名字,于是报错。

既然exec创建的变量在默认的局部空间里,我们就要想办法进入那个空间去拿!这就需要用到两个内置字典:

locals: 返回当前局部作用域的变量字典。globals: 返回当前全局作用域的变量字典。

改进版写法:

def create_var_dynamic(var_name, value): # 1. 准备要执行的代码字符串 code = f"{var_name} = {value}" # 注意:这里字符串拼接有风险!后面讲 # 2. 获取函数当前的局部作用域字典 local_scope = {} # 3. 关键一步:把空字典 local_scope 同时传给 locals 和 globals 参数 exec(code, globals, local_scope) # 4. 现在,变量存在于 local_scope 这个字典里了! return local_scope[var_name] # 从字典里取出我们创建的变量值# 使用var_name = "calculated_total" # 动态变量名result = create_var_dynamic(var_name, 11 + 22) # 动态值print(result) # 输出: 33# 注意!在函数外部,变量名 `calculated_total` 并不存在!# 我们只是通过函数返回了它的值。

关键解释:

1、我们创建了一个空的字典 local_scope = {}。 2、调用 exec(code, globals, local_scope) 时:

globals 获取了全局作用域(提供基础环境)。local_scope 这个空字典被用作执行代码的局部命名空间。exec 执行 code 时,所有在代码里创建的变量(如 calculated_total)都会被塞进 local_scope 这个字典里。 3、 执行完毕后,我们想要的变量值就安静的躺在 local_scope[var_name] 里,直接返回它即可。

上面解决了作用域问题,但exec最大的“坑”才刚浮出水面——极其危险的安全漏洞!

问题根源:exec 会无条件执行你传给它的任何字符串代码!如果这个字符串来自不可信的来源(用户输入、网络、文件),攻击者可以构造恶意字符串。

恐怖示例:

# 假设用户输入恶意值user_input = "100; import os; os.system('rm -rf /')" # 模拟删除系统文件(极端危险!)# 或者更隐蔽的: user_input = "100; __import__('os').system('malicious_command')"# 如果直接拼接到execresult = create_var_dynamic("hacked", user_input)# 执行后,不仅返回值,你的系统可能已经被攻击了!

exec(f"{var_name} = {user_input}") 会把用户输入的 import os; os.system(...) 也当作代码执行!后果不堪设想。

为什么字典 (dict) 是更好的选择?

对于动态命名需求,99%的情况,字典都是更安全、更清晰、更高效的选择!

def process_data(data, prefix): # 创建一个空字典存储结果 results = {} # ... 处理data ... # 动态键名:完全满足“动态命名”需求 key = prefix + "_result" results[key] = processed_data return results # 安全返回整个字典# 使用data = {...}output = process_data(data, "user")user_result = output["user_result"] # 安全获取你想要的值

优势:

安全: 字典的键只是数据,不会被当作代码执行。清晰: 所有动态“变量”都规规矩矩待在字典里,结构一目了然。灵活: 轻松存储、修改、遍历各种动态命名的数据。作用域友好: 没有exec那些诡异的作用域问题。

Python的exec就像一把锋利的“瑞士军刀”,功能强大但也容易割伤自己。当你真正成长为Python高手,理解了代码执行的底层原理和安全风险的方方面面,再谨慎地考虑是否真的需要动用exec这把“双刃剑”吧!安全第一,快乐编码!

来源:信息科技云课堂

相关推荐