Python将字符串常量转化为变量方法总结


Posted in Python onMarch 17, 2019

前几天,我们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 是你凭空定义出来的,而不是从已有条件中生成的。

当时,群里只有两三个同学参与了讨论,我们没想到解决办法。但是,我觉得这个题目很有意思,值得玩味。

因为,如果能解决这个问题,那就意味着可以不作预先定义,而是动态地生成变量名,这不仅能减少给变量取名的麻烦,还实现了自动编码!

可以设想一下未来,人工智能在编写代码的时候,如果能根据已知条件,动态生成变量名,那编写代码的过程不就顺利多了么?(据说,现在已经有人工智能可以编写代码了,不知它在取变量名时,是用的什么方法?)

2、办法总是有的

最近,学习群里蒙混进来了几个打广告的,为此,我决定提高审核门槛,例如,用群里的问题来作个考核。

万万没想到的是,第一个被考核到的 Q 同学,几乎不假思索地就说出了一个解决上述问题的思路。而偏偏就是那么巧 ,几乎在同时,群内的 J 同学给出了另外一个解决方法(他没看到群内的讨论,而是看到了知识星球的记录,才知道这个问题的)。

也就是说,前一晚还以为无解的问题,在第二天竟得到了两种不同的解决方法!

那么,他们的答案是什么呢?

# J 同学的解答
>>> list1 = ['A', 'B', 'C', 'D']
>>> for i in list1:
>>> globals()[i] = []
>>> A
[]

这个方法通过修改全局命名空间,巧妙地“定义”出了新的变量。globals() 方法取出来的是一个字典,字符串 ‘A' 是其中一个键值(key),而这个键值恰恰是全局命名空间中的一个变量,这就实现了从常量到变量的转化。

在数据结构层面上,空列表 [] 作为一个值(value)跟它的字符串键值绑定在一起,而在运用层面上,它作为变量内容而跟变量名绑定在一起。

看到这个回答的时候,我就突然想起来了,上个月转载过一篇《Python 动态赋值的陷阱》,讲的正是动态地进行变量赋值 的问题啊!我似乎只关注了 globals() 与 locals() 用法的区别,却没有真正地掌握它们的原初用途。

J 同学说,他正是看了那篇文章,才学得了这个方法。这就有意思了,我分享了一个自己囫囵吞枣的知识,然后它被 J 同学吸收掌握,最后反馈回来解决了我的难题。

我真切地感受到了知识分享的魅力:知识在流动中获得生命,在碰撞中锃亮色泽。

同时,我也真切地明白了一个互助的学习团体的好处:利人者也利己,互助者共同进步。

3、动态执行代码的方法

新进群的 Q 同学,提供了一个不同的答案:

# 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() 方法,它是内置的,用途是执行储存在字符串或文件中的代码段。

它的基础用法如下:

>>> 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() 的安全用法……

虽然,某些话题无法在群聊中充分展开,但是,这些话题知识的延展联系,大大地丰富了本文开头的问题,这一个微小的问题,牵连出来了两个大的知识体系。

最后,真得感谢群内的这些爱学习的优秀的同志们!除了文中提及的,还有一些同学也做了积极贡献,大家都很给力!

Python 相关文章推荐
windows 10下安装搭建django1.10.3和Apache2.4的方法
Apr 05 Python
酷! 程序员用Python带你玩转冲顶大会
Jan 17 Python
Python通过属性手段实现只允许调用一次的示例讲解
Apr 21 Python
利用Python如何将数据写到CSV文件中
Jun 05 Python
Python使用cx_Freeze库生成msi格式安装文件的方法
Jul 10 Python
python删除字符串中指定字符的方法
Aug 13 Python
python ftp 按目录结构上传下载的实现代码
Sep 12 Python
numpy 返回函数的上三角矩阵实例
Nov 25 Python
pandas 对group进行聚合的例子
Dec 27 Python
pytorch对梯度进行可视化进行梯度检查教程
Feb 04 Python
python filecmp.dircmp实现递归比对两个目录的方法
May 22 Python
Python txt文件常用读写操作代码实例
Aug 03 Python
实例讲解Python中整数的最大值输出
Mar 17 #Python
python3+selenium自动化测试框架详解
Mar 17 #Python
Django 中间键和上下文处理器的使用
Mar 17 #Python
Python时间和字符串转换操作实例分析
Mar 16 #Python
Python中numpy模块常见用法demo实例小结
Mar 16 #Python
Python常见的pandas用法demo示例
Mar 16 #Python
详解python中list的使用
Mar 15 #Python
You might like
php 批量替换程序的具体实现代码
2013/10/04 PHP
PHP封装分页函数实现文本分页和数字分页
2014/10/23 PHP
php+xml实现在线英文词典之添加词条的方法
2015/01/23 PHP
yii2项目实战之restful api授权验证详解
2017/05/20 PHP
PHP实现广度优先搜索算法(BFS,Broad First Search)详解
2017/09/16 PHP
PHP实现打包下载文件的方法示例
2017/10/07 PHP
深入理解PHP中mt_rand()随机数的安全
2017/10/12 PHP
php对象工厂类完整示例
2018/08/09 PHP
深入理解javascript中的立即执行函数(function(){…})()
2014/06/12 Javascript
基于jquery实现select选择框内容左右移动添加删除代码分享
2015/08/25 Javascript
jQuery实现默认是闭合的FAQ展开效果菜单
2015/09/14 Javascript
vue.js指令v-for使用及索引获取
2016/11/03 Javascript
jQuery实现在新增加的元素上添加事件方法案例分析
2017/02/09 Javascript
Vue 2.5 Level E 发布了: 新功能特性一览
2017/10/24 Javascript
SVG实现时钟效果
2018/07/17 Javascript
ES6知识点整理之对象解构赋值应用示例
2019/04/17 Javascript
分享一个vue项目“脚手架”项目的实现步骤
2019/05/26 Javascript
javascript实现弹出层效果
2019/12/10 Javascript
《javascript设计模式》学习笔记七:Javascript面向对象程序设计组合模式详解
2020/04/08 Javascript
使用python装饰器计算函数运行时间的实例
2018/04/21 Python
pytorch 数据集图片显示方法
2018/07/26 Python
Python3.5以上版本lxml导入etree报错的解决方案
2019/06/26 Python
Python enumerate函数遍历数据对象组合过程解析
2019/12/11 Python
基于Python数据结构之递归与回溯搜索
2020/02/26 Python
Django 自定义404 500等错误页面的实现
2020/03/08 Python
解决keras backend 越跑越慢问题
2020/06/18 Python
CSS3 border-radius圆角的实现方法及用法详解
2020/09/14 HTML / CSS
海淘母婴商城:国际妈咪
2016/07/23 全球购物
英国DIY汽车维修配件网站:DIY Car Service Parts
2019/08/30 全球购物
英国现代家具和照明购物网站:Heal’s
2019/10/30 全球购物
简述你对Statement,PreparedStatement,CallableStatement的理解
2013/03/25 面试题
就业协议书的作用
2014/04/11 职场文书
专业技术职务聘任证明
2015/03/02 职场文书
学校扫黄打非工作总结
2015/10/15 职场文书
Spring Boot两种全局配置和两种注解的操作方法
2021/06/29 Java/Android
MySQL修炼之联结与集合浅析
2021/10/05 MySQL