Python将字符串常量转化为变量⽅法总结
前⼏天,我们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)跟它的字符串键值绑定在⼀起,⽽在运⽤层⾯上,它作为变量内容⽽跟变
量名绑定在⼀起。
看到这个回答的时候,我就突然想起来了,上个⽉转载过⼀篇《》,讲的正是动态地进⾏变量赋值的问题啊!我似乎只关注了 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, '= []']))
这⼏种写法的区别只是字符串拼接法的区别,关于如何拼接字符串,以及不同⽅法之间的区别,可参看《》。
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() 的安全⽤法……
虽然,某些话题⽆法在聊中充分展开,但是,这些话题知识的延展联系,⼤⼤地丰富了本⽂开头的问题,这⼀个微⼩的问题,牵连出来了两个⼤的知识体系。字符串常量使用一对什么界定若干个字符
最后,真得感谢内的这些爱学习的优秀的同志们!除了⽂中提及的,还有⼀些同学也做了积极贡献,⼤家都很给⼒!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论