想根据用户输入、配置文件或者其他动态条件,在函数里灵活地创建不同名字的变量?
很多Python萌新会想到exec()这个“魔法”函数。它确实能“变”出变量,但也像一把双刃剑,用不好会伤到自己!今天就来聊聊exec()在函数内设置变量的门道和那些你必须知道的“坑”。
一、 什么是exec()?它能干嘛简单说,exec()是一个Python内置函数。它的“超能力”是:能把一个字符串当作完整的Python代码来执行! 就像你写在.py文件里的代码一样。
想象一下:你有一个字符串 "x = 11 + 22"。如果你把它丢给exec():
exec("x = 11 + 22") print(x) # 输出 33神奇吗?x这个变量就被创建并赋值了!它似乎打破了“变量必须先定义后使用”的常规。
二、 为什么有人想在函数里用exec()造变量核心诉求是:动态变量名!
场景举例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()创建的变量既然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()最大的“坑”才刚浮出水面——极其危险的安全漏洞!
问题根源: exec() 会无条件执行你传给它的任何字符串代码!如果这个字符串来自不可信的来源(用户输入、网络、文件),攻击者可以构造恶意字符串。
恐怖示例:
# 假设用户输入恶意值 user_input = "100; import os; os.system('rm -rf /')" # 模拟删除系统文件(极端危险!) # 或者更隐蔽的: user_input = "100; __import__('os').system('malicious_command')" # 如果直接拼接到exec result = 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()这把“双刃剑”吧!安全第一,快乐编码!