Python进阶:如何将字符串常量转化为变量?

前几天,咱们Python猫交流学习群 里的 M 同窗提了个问题。这个问题挺有意思,经初次讨论,咱们认为它无解。安全

然而,我认为它颇有价值,应该继续思考怎么解决,因此就在私密的知识星球上记录了下来。bash

万万没想到的是,在次日,有两位同窗接连给出了解决方法!数据结构

由此,群内出现了一轮热烈的技术交流。函数

本文将相关的内容要点做了梳理,并由此引伸到更进一步的学习话题,但愿对你有所帮助。学习

一、如何动态生成变量名?

M 同窗的问题以下:ui

打扰一下你们,请教一个问题,已知 list = ['A', 'B', 'C', 'D'] , 如何才能获得以 list 中元素命名的新列表 A = [], B = [], C = [], D = [] 呢?编码

简单理解,这个问题的意思是,将字符串内容做为其它对象的变量名。人工智能

list 中的元素是字符串,此处的 ‘A’-‘D’ 是常量 ,而在要求的结果中,A-D 是变量spa

若是强行直接将常量当作变量使用,它会报错:翻译

>>> 'A' = []
...SyntaxError: can't assign to literal 复制代码

报错中的literal 指的是字面量 ,这是计算机科学中常见的一个概念,用于表达源代码中的固定值。 例如,整数、浮点数、字符串等基本类型,就是字面量。

字面量指的就是一个量自己,能够理解为一种原子性的实体,固然不能再被赋值了。

因此,取出的字符串内容,并不能直接用做变量名,须要另想办法。

有初学者可能会想,list[0] = [] 行不行?固然不行,由于没有出现 A 。那 A = list[0] ,接着 A = [] 呢?那也不行,由于这里的 A 是你凭空定义出来的,而不是从已有条件中生成的。

当时,群里只有两三个同窗参与了讨论,咱们没想到解决办法。可是,我以为这个题目颇有意思,值得玩味。

由于,若是能解决这个问题,那就意味着能够不做预先定义,而是动态地生成变量名,这不只能减小给变量取名的麻烦,还实现了自动编码!

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

二、办法老是有的

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

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

也就是说,前一晚还觉得无解的问题,在次日竟获得了两种不一样的解决方法!

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

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

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

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

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

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

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

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

三、动态执行代码的方法

新进群的 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。

四、总结

抽象一下最初的问题,它实际问的是“如何将字符串内容做为其它对象的变量名”,更进一步地讲是——“如何将常量转化为变量 ”。

使用直接进行赋值的静态方法,行不通。

两位同窗提出的方法都是间接的动态方法:一个是动态地进行变量赋值,经过修改命名空间而植入变量;一个是动态地执行代码,能够说是经过“走后门”的方式,安插了变量。

两种方法异曲同工,不论是白猫仍是黑猫,它们都抓到了老鼠。

这两种方法已经给咱们带来了颇有价值的启发,同时,由于它们,群内小伙伴们更是发散地讨论一些相关联的话题,例如:S 同窗提出了另外一种修改命名空间中变量的写法、L 同窗提到了 eval() 的意义、eval() 与 exec() 的区别、我查到了为何要慎用 eval() 、C 与 H 同窗提到了 eval() 的安全用法......

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

最后,真得感谢群内的这些爱学习的优秀的同志们!除了文中说起的,还有一些同窗也作了积极贡献,你们都很给力!

相关连接:

Python 动态赋值的陷阱

详解Python拼接字符串的七种方式

eval()、exec()及其相关函数:www.tuicool.com/wx/vEbeumE

公众号【Python猫】, 专一Python技术、数据科学和深度学习,力图创造一个有趣又有用的学习分享平台。本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、优质英文推荐与翻译等等,欢迎关注哦。PS:后台回复“爱学习”,免费得到一份学习大礼包。

相关文章
相关标签/搜索