在Python编程中,动态命名变量指的是变量名不是硬编码的固定字符串,而是可以根据运行时的条件动态生成的,比如变量名是拼接字符串、从配置中读取的名称等,此时需要找到合适的方式访问这些变量对应的数值。

为什么不建议直接拼接变量名访问
很多刚接触Python的开发者会尝试用字符串拼接的方式生成变量名,然后直接调用,比如下面这种写法:
# 错误示例,这种写法会直接报错 var_name = "test" test = 123 print(var_name) # 这里只会输出字符串test,不是变量test的值 # 如果尝试直接拼接调用,会报NameError # print(eval(var_name)) # 虽然能运行,但eval有安全风险,不推荐
这种写法不仅不规范,还可能存在代码注入的安全风险,因此我们需要更优雅的替代方案。
方案一:使用locals和globals函数
locals函数可以返回当前作用域的局部变量字典,globals函数返回全局变量字典,我们可以通过变量名字符串作为键来访问对应的值。
访问全局动态变量
如果动态变量是定义在全局作用域的,可以使用globals函数:
# 定义全局变量
user_name = "张三"
user_age = 25
user_city = "北京"
# 动态变量名前缀
prefix = "user_"
# 需要访问的变量后缀列表
suffix_list = ["name", "age", "city"]
for suffix in suffix_list:
var_key = prefix + suffix
# 从全局变量字典中获取值
value = globals().get(var_key)
print(f"{var_key}对应的值是:{value}")访问局部动态变量
如果变量是在函数内部定义的局部变量,可以使用locals函数:
def get_dynamic_var():
# 函数内的局部变量
score_math = 95
score_chinese = 88
score_english = 92
# 动态变量名前缀
prefix = "score_"
subjects = ["math", "chinese", "english"]
for subject in subjects:
var_key = prefix + subject
# 从局部变量字典中获取值
value = locals().get(var_key)
print(f"{var_key}对应的成绩是:{value}")
get_dynamic_var()注意:locals函数返回的字典是局部变量的副本,修改这个字典不会影响实际的局部变量,只适合用来读取值。
方案二:使用getattr访问对象属性
如果动态变量是某个对象的属性,我们可以使用getattr函数来访问,这种方式比直接操作变量字典更安全规范。
class User:
def __init__(self):
self.name = "李四"
self.age = 30
self.email = "lisi@ippipp.com"
user = User()
# 动态属性名
attr_name = "name"
# 使用getattr获取属性值,第三个参数是默认值,属性不存在时返回
value = getattr(user, attr_name, "属性不存在")
print(f"user的{attr_name}属性值是:{value}")
# 批量获取属性
attr_list = ["age", "email", "phone"]
for attr in attr_list:
val = getattr(user, attr, None)
print(f"user的{attr}属性值是:{val}")方案三:使用字典存储动态变量(推荐)
实际上,如果我们提前知道变量是动态命名的,最优雅的方式从一开始就用字典来存储这些变量,而不是定义一堆独立的不同名变量,这样访问时直接通过键获取即可,可读性和可维护性都更好。
# 用字典存储动态变量,键就是动态变量名,值是对应内容
dynamic_vars = {
"config_host": "127.0.0.1",
"config_port": 8080,
"config_timeout": 30
}
# 动态键名
key_to_get = "config_port"
# 直接通过键访问,不存在时返回默认值
value = dynamic_vars.get(key_to_get, None)
print(f"配置项{key_to_get}的值是:{value}")
# 批量处理动态键
key_list = ["config_host", "config_timeout", "config_retry"]
for key in key_list:
val = dynamic_vars.get(key)
print(f"配置{key}:{val}")各方案对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| globals/locals | 已有大量独立动态变量,需要读取的场景 | 不需要修改原有变量定义方式 | 可读性差,容易引发作用域问题 |
| getattr | 动态访问对象属性的场景 | 符合面向对象编程规范,安全可控 | 仅适用于对象属性,不适用于普通变量 |
| 字典存储 | 可以提前规划变量结构的场景 | 可读性强,易维护,访问高效 | 需要提前调整变量存储方式 |
注意事项
- 尽量避免使用
eval和exec函数来执行动态生成的变量名字符串,这两个函数有严重的安全风险,容易被注入恶意代码。 - 如果使用locals函数,要注意它在不同作用域下的表现可能不一致,比如在类的方法中,locals不会包含类实例的属性。
- 新开发的功能如果涉及动态命名的变量,优先选择字典存储的方案,从根源上避免动态访问变量的需求。