DDR爱好者之家 Design By 杰米

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

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

万万没想到的是,在第二天,有两位同学接连给出了解决方法!

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

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

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

M 同学的问题如下:

打扰一下大家,请教一个问题,已知 list = ['A', 'B', 'C', 'D'] , 如何才能得到以 list 中元素命名的新列表 A = [], B = [], C = [], D = [] 呢"htmlcode">

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

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

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

DDR爱好者之家 Design By 杰米
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
DDR爱好者之家 Design By 杰米

稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!

昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。

这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。

而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?