python 关于字典dict的fromkeys方法的巨坑,真的只有踩过之后才知道

本文试验的python版本为3.6.2,先来看下python3.6官方文档上对fromkeys的描述,极其简单:
在这里插入图片描述
看似简单方便,实则很坑!

在某些场景下,需要初始化一个字典,键是确定的,但是值是需要等待去填充或修改的,正好字典自带的fromkeys方法似乎刚好可以做到这个事情,并可以初始化所有键的值。

但是fromkeys有一个巨坑,真的只有踩过之后才知道,请耐心往下看

首先先看下这个坑是怎么产生以及如何坑人的。

1. 踩坑重现(错误示范)

1.1 通过fromkeys初始化一个空的字典

d = dict.fromkeys(['a', 'b', 'c'], {'score':0, 'type': []})
print(d)

初始化字典如下:

{
	'a': {'score': 0, 'type': []}, 
	'b': {'score': 0, 'type': []}, 
	'c': {'score': 0, 'type': []}
}

1.2 修改某一键下的值

d['a']['score'] = 3
d['b']['type'].append('type b')
print(d)
{
	'a': {'score': 3, 'type': ['type b']}, 
	'b': {'score': 3, 'type': ['type b']}, 
	'c': {'score': 3, 'type': ['type b']}
}

发现没有!通过fromkeys创建的初始字典,当修改某一键下的值时,所有的值都会随之修改!

这是因为通过fromkeys创建初始字典时,所设定的默认值,是存储在同一块存储空间上的,相当于所有的值都是同一个值,类似于浅拷贝。
关于深浅拷贝,可以参考我的这篇文章,解释得应该很清楚了:

python变量的引用赋值及深浅拷贝

2. 初始化字典的正确方法

所以如想正确初始化一个字典,应该采用类似于列表推导式的方法,如下:

d = {key: {'score': 0, 'type': []} for key in ['a', 'b', 'c']}
print(d)

可以看到创建的字典效果一样的

{
	'a': {'score': 0, 'type': []}, 
	'b': {'score': 0, 'type': []}, 
	'c': {'score': 0, 'type': []}
}

并且可以正常修改键值:

d['a']['score'] = 3
d['b']['type'].append('type b')
print(d)
{
	'a': {'score': 3, 'type': []}, 
	'b': {'score': 0, 'type': ['type b']}, 
	'c': {'score': 0, 'type': []}
}