【水蓝石】python 字典fromkeys深度理解和踩坑

2022-07-25   385 次阅读


在实际的工作中想要完成一个这样的需求:

在一个长列表
['1', '1', '1', '0', '2', '2', '2', '2', '2', '2', '2', '2', '0', '1', '1', '1', '1', '0', '1', '1', '0', '10', '0', '11', '11', '11', '11', '0', '6', '0']
中记录每个相同元素的分块后的起始位置,并方便后面取用。为了完成这样的一个需求选用了字典的方式进行处理。以元素当做key而各自块的起始位置当做values。在调查中发现{}.fromkeys(seq,defaultValue)可以以很快的速度同时完成去重和建立key为列表元素value为空列表的操作。样例如下:

dict.fromkeys(['1', '1', '1', '0', '2', '2', '2', '2', '2', '2', '2', '2', '0', '1', '1', '1', '1', '0', '1', '1', '0', '10', '0', '11', '11', '11', '11', '0', '6', '0'],[])
{'1': [], '0': [], '2': [], '10': [], '11': [], '6': []}

这样再只记录不同元素的起始位置,append到指定的dict的位置就可以简单快速的完成需求。

本来是这样想的,但是当真的向用fromkeys创建出的字典添加元素时却发生了这样的事情:

Dict = dict.fromkeys(['1', '1', '1', '0', '2', '2', '2', '2', '2', '2', '2', '2', '0', '1', '1', '1', '1', '0', '1', '1', '0', '10', '0', '11', '11', '11', '11', '0', '6', '0'],[])
Dict
{'1': [], '0': [], '2': [], '10': [], '11': [], '6': []}

Dict['1'].append(3)

Dict
{'1': [3], '0': [3], '2': [3], '10': [3], '11': [3], '6': [3]}

只想向其中指定某键的列表添加元素,但所有键的列表都发生了改变,导致了程序出现bug。

第一时间发生的时候有点不太理解,但聪明的读者结合python语言的性质仔细一想大概就能明白发生了什么了。fromkeys之所以会比我自己写for循环快那么多,就是因为他在建立新的字典的时候在字典键值连接时使用了浅拷贝。也就是说字典中的键都指向了同一个内存地址,所以我通过字典中一个键指向的地址来对内存进行改变时,实际上就改变了所有键的值。

所以fromkeys主要还是用于快速建立一个所有值都相同的字典时使用,像我这样使用是不恰当的。

那为了完成需求,有没有什么代替的写法呢?

这时就不能偷懒,还是for循环遍历每个元素依次给予地址了。

badDict = dict.fromkeys(['1', '1', '1', '0', '2', '2', '2', '2', '2', '2', '2', '2', '0', '1', '1', '1', '1', '0', '1', '1', '0', '10', '0', '11', '11', '11', '11', '0', '6', '0'],[])

goodDict = {key: [] for key in ['1', '1', '1', '0', '2', '2', '2', '2', '2', '2', '2', '2', '0', '1', '1', '1', '1', '0', '1', '1', '0', '10', '0', '11', '11', '11', '11', '0', '6', '0']}

Dict
{'1': [], '0': [], '2': [], '10': [], '11': [], '6': []}
dict5
{'1': [], '0': [], '2': [], '10': [], '11': [], '6': []}

只是时间上会多花一些,但也是必要的

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

无论在未来前做什么,未来都会普通的到来