Python将字符串常量转化为变量方法总结
本文探讨Python中将字符串常量动态转化为变量名的两种核心方法。通过修改全局命名空间(globals)和动态执行代码(exec),实现从字符串到变量的映射。文章详细解析了两种方案的原理、代码示例及注意事项,旨在解决动态生成变量名的技术难题,并引申出对eval与exec区别及安全性的讨论。
引言:动态生成变量名的挑战
日前,Python技术交流社群中M同学提出一个颇具趣味性的问题。经过初步探讨,大家普遍认为该问题难以直接解决。
但我认为此问题具有研究价值,值得深入思考解决方案,因此将其记录在私密的知识社区中。
出乎意料的是,次日便有两位社群成员接连提供了可行的解决思路。
这一事件在群内引发了热烈的技术探讨与交流。
本文对相关内容进行了梳理,并由此延伸出更深入的学习话题,期望能为读者提供有益参考。
问题背景:从常量到变量的转化
1、如何动态生成变量名?
M同学的具体问题如下:
打扰一下大家,请教一个问题,已知 list = ['A', 'B', 'C', 'D'] , 如何才能得到以 list 中元素命名的新列表 A = [], B = [], C = [], D = [] 呢?
简而言之,该问题的核心在于将字符串内容用作其他对象的变量名。
list中的元素为字符串,此处‘A'-‘D'属于常量,而目标结果中A-D需表现为变量。
若强行直接将常量当作变量使用,程序会抛出错误:
>>> 'A' = [] ...SyntaxError: can't assign to literal
报错信息中的literal指代字面量,这是计算机科学中的常见概念,用于表示源代码中的固定值。例如整数、浮点数、字符串等基本类型均属于字面量。
字面量代表一个量的本身,可理解为一种原子性实体,因此无法对其进行赋值操作。
因此,提取出的字符串内容不能直接作为变量名,必须寻找其他途径。
初学者可能会尝试list[0] = [],但这不可行,因为并未生成名为A的变量。若尝试A = list[0],接着A = [],同样行不通,因为这里的A是凭空定义的,而非从已有条件中动态生成的。
当时,群内仅有两三位同学参与讨论,我们未能找到解决办法。但我认为此题目颇具玩味价值。
若能解决此问题,意味着无需预先定义即可动态生成变量名,这不仅能减少命名负担,还能实现自动化编码!
可以设想未来,人工智能在编写代码时,若能根据已知条件动态生成变量名,编码过程将更加顺畅。(据悉,目前已有AI具备代码编写能力,不知其在变量命名时采用何种方法?)
解决方案一:修改全局命名空间
2、办法总是有的
近期,学习群中混入了一些广告发布者,为此我决定提高审核门槛,例如以群内问题作为考核标准。
出乎意料的是,首位被考核的Q同学几乎不假思索地提出了一个解决思路。巧合的是,几乎同时,群内J同学也给出了另一种解决方法(他未看到群内讨论,而是通过知识社区记录得知此问题)。
也就是说,前一晚看似无解的问题,在次日竟得到了两种不同的解决方案!
那么,他们的具体答案是什么呢?
globals()方法实现示例
# J 同学的解答
>>> list1 = ['A', 'B', 'C', 'D']
>>> for i in list1:
>>> globals()[i] = []
>>> A
[]
该方法通过修改全局命名空间,巧妙地“定义”出新变量。globals()方法返回一个字典,字符串‘A'作为其中一个键值(key),而该键值恰好对应全局命名空间中的一个变量,从而实现了从常量到变量的转化。
在数据结构层面,空列表[]作为值(value)与字符串键值绑定;在应用层面,它作为变量内容与变量名绑定。
看到此回答时,我突然想起上个月转载过一篇《Python 动态赋值的陷阱》,讲述的正是动态变量赋值问题!我此前似乎只关注了globals()与locals()用法的区别,并未真正掌握它们的原始用途。
J同学表示,他正是通过阅读那篇文章习得此方法。这很有趣:我分享了一个自己一知半解的知识,随后被J同学吸收掌握,最终反馈回来解决了我的难题。
我深切感受到知识分享的魅力:知识在流动中获得生命,在碰撞中熠熠生辉。
同时,我也深刻体会到互助学习团体的益处:利人者利己,互助者共同进步。
解决方案二:动态执行代码
3、动态执行代码的方法
新加入群的Q同学提供了另一种答案:
exec()与f-strings应用示例
# Q 同学的解答
>>> list1 = ['A', 'B', 'C', 'D']
>>> for i in list1:
>>> exec(f"{i} = []")
>>> A
[]
他的写法利用了Python 3.6引入的f-strings特性。事实上,在较低版本中也可实现,只需确保exec()方法接收的参数是包含变量i的字符串即可,例如这样写:
兼容低版本的字符串拼接写法
# 以下代码可替换上例的第 4 行
exec(i + " = []")
# 或者:
exec("{} = []".format(i))
# 或者:
exec(' '.join([i, '= []']))
这几种写法的区别仅在于字符串拼接方式,关于如何拼接字符串以及不同方法间的区别,可参考《详解Python拼接字符串的七种方式》。
Q同学答案的核心在于exec()方法,它是Python内置函数,用于执行存储在字符串或文件中的代码段。
其基础用法如下:
exec()基础用法说明
>>> exec('x = 1 + 2')
>>> x
3
# 执行代码段
>>> s = """
>>> x = 10
>>> y = 20
>>> sum = x + y
>>> print(sum)
>>> """
>>> exec(s)
30
了解exec()用法后,我们再回顾Q同学的答案。for循环中提取的i为字符串,拼接后的字符串经exec()处理,便获得了动态编写代码的效果。
也就是说,因字符串常量的内容被当作有效代码执行,其中的'A'-'D'元素便获得了新身份,转化为最终的A-D变量名。
该方法看似简单,但由于exec()方法较为生僻,直到Q同学提出,我们才恍然大悟。
注意:在Python3中,exec()是内置方法;而在Python2中,exec是语句(statement),另有execfile()方法,两者合并后形成Python3中的exec()。本文基于Python3编写。
总结与延伸讨论
4、总结
抽象最初的问题,其实际询问的是“如何将字符串内容作为其他对象的变量名”,更进一步讲是——“如何将常量转化为变量”。
使用直接赋值的静态方法行不通。
两位同学提出的方法均为间接动态方法:一是动态进行变量赋值,通过修改命名空间植入变量;二是动态执行代码,可谓通过“走后门”的方式安插变量。
两种方法殊途同归,无论白猫黑猫,只要抓到老鼠就是好猫。
这两种方法已带来极具价值的启发,并促使群内伙伴发散讨论相关话题,例如:S同学提出另一种修改命名空间中变量的写法、L同学提及eval()的意义、eval()与exec()的区别、我查阅了为何慎用eval()、C与H同学探讨了eval()的安全用法……
尽管某些话题无法在群聊中充分展开,但这些话题知识的延展联系,极大丰富了本文开头的问题。这一个微小问题,牵连出两个大的知识体系。
最后,真诚感谢群内这些爱学习的优秀同志们!除文中提及者外,还有其他同学做出积极贡献,大家表现都非常给力!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













