[Python] 关于字典方法 fromkeys() 的踩坑和理解
fromkeys()方法以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值。语法:dict.fromkeys(seq,value=None)参数:seq -- 字典键值列表。value -- 可选参数, 设置键序列(seq)对应的值,默认为 None。返回值:返回一个新字典。[例]想要创建一个这样的字典:>...
fromkeys()方法以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值。
语法:
dict.fromkeys(seq,value=None)
参数:
seq -- 字典键值列表。
value -- 可选参数, 设置键序列(seq)对应的值,默认为 None。
返回值:
返回一个新字典。
[例]
想要创建一个这样的字典:
>>> dict_28star={'青龙': ['角', '亢', '氐', '房', '心', '尾', '箕'], \
'玄武': ['斗', '牛', '女', '虚', '危', '室', '壁'], \
'白虎': ['奎', '娄', '胃', '昴', '毕', '参', '觜'], \
'朱雀': ['井', '鬼', '柳', '星', '张', '翼', '轸']}
>>>
>>> dict_28star
{'青龙': ['角', '亢', '氐', '房', '心', '尾', '箕'], '玄武': ['斗', '牛', '女', '虚', '危', '室', '壁'], '白虎': ['奎', '娄', '胃', '昴', '毕', '参', '觜'], '朱雀': ['井', '鬼', '柳', '星', '张', '翼', '轸']}
使用方法fromkeys()如下:
1.创建时不赋值,值为None
>>> dict1={}
>>> seq = ('青龙', '玄武', '白虎','朱雀')
>>> dict1=dict1.fromkeys(seq)
>>> dict1
{'青龙': None, '玄武': None, '白虎': None, '朱雀': None}
>>>
然后依次赋值,
>>> dict1['青龙']=['角', '亢', '氐', '房', '心', '尾', '箕']
>>> dict1['玄武']=['斗', '牛', '女', '虚', '危', '室', '壁']
>>> dict1['白虎']=['奎', '娄', '胃', '昴', '毕', '参', '觜']
>>> dict1['朱雀']=['井', '鬼', '柳', '星', '张', '翼', '轸']
>>> dict1
{'青龙': ['角', '亢', '氐', '房', '心', '尾', '箕'], '玄武': ['斗', '牛', '女', '虚', '危', '室', '壁'], '白虎': ['奎', '娄', '胃', '昴', '毕', '参', '觜'], '朱雀': ['井', '鬼', '柳', '星', '张', '翼', '轸']}
>>>
可以看到得到的是想要的效果,这里顺便查以下4个值对应的内存地址(会发现是不一样的地址),方便稍后比较
>>> for v in dict1.values(): print(id(v))
1533561286792
1533564252744
1533561281480
1533561281096
>>>
2.创建时赋值,4个键会共用一个值,准确来说是指向同一个位置,可以用id(value)看4个值指向的内存位置,发现是一样的
>>> dict2={}
>>> dict2=dict2.fromkeys(seq,['角', '亢', '氐', '房', '心', '尾', '箕'])
>>> dict2
{'青龙': ['角', '亢', '氐', '房', '心', '尾', '箕'], '玄武': ['角', '亢', '氐', '房', '心', '尾', '箕'], '白虎': ['角', '亢', '氐', '房', '心', '尾', '箕'], '朱雀': ['角', '亢', '氐', '房', '心', '尾', '箕']}
>>>
>>> for v in dict2.values(): print(id(v))
1533561281288
1533561281288
1533561281288
1533561281288
>>>
尝试更新 '玄武' 键的值,更新成功,查看其内存地址,指向了别处。
>>> dict2['玄武']=['斗', '牛', '女', '虚', '危', '室', '壁']
>>> dict2
{'青龙': ['角', '亢', '氐', '房', '心', '尾', '箕'], '玄武': ['斗', '牛', '女', '虚', '危', '室', '壁'], '白虎': ['角', '亢', '氐', '房', '心', '尾', '箕'], '朱雀': ['角', '亢', '氐', '房', '心', '尾', '箕']}
>>> for v in dict2.values(): print(id(v))
1533561281288
1533564331336
1533561281288
1533561281288
>>>
用同样的方法可以更新 '白虎' 键的值和 '朱雀' 键的值。
3.看着没什么问题,再玩一玩会发现有坑的,例如我们不想用这种直接赋值的方法改变键的值,就是想用用给append()或者给二级元素一个个赋值的方法呢?下面踩踩坑~
使用 fromkeys() 赋值的列表里只有一个值,创建好字典后再append。这里先考虑 '青龙',给它的值(一个列表)append第二个元素 '亢',然后会发现对4个值做了一样的操作。
>>> dict3={}
>>> dict3=dict3.fromkeys(seq,['角'])
>>> dict3
{'青龙': ['角'], '玄武': ['角'], '白虎': ['角'], '朱雀': ['角']}
>>> dict3['青龙'].append('亢')
>>> dict3
{'青龙': ['角', '亢'], '玄武': ['角', '亢'], '白虎': ['角', '亢'], '朱雀': ['角', '亢']}
>>> for v in dict3.values(): print(id(v))
1533564587528
1533564587528
1533564587528
1533564587528
>>>
给值(列表)中的元素赋值,4个列表也一起变
>>> dict3['玄武'][0]='斗'
>>> dict3
{'青龙': ['斗', '亢'], '玄武': ['斗', '亢'], '白虎': ['斗', '亢'], '朱雀': ['斗', '亢']}
>>> for v in dict3.values(): print(id(v))
1533564587528
1533564587528
1533564587528
1533564587528
>>>
对值(列表)使用 pop(),4个列表也一起变
>>> dict3['白虎'].pop()
'亢'
>>> dict3
{'青龙': ['斗'], '玄武': ['斗'], '白虎': ['斗'], '朱雀': ['斗']}
>>> for v in dict3.values(): print(id(v))
1533564587528
1533564587528
1533564587528
1533564587528
>>>
将值(列表)清空,4个列表也一起清空
>>> dict3['朱雀'].clear()
>>> dict3
{'青龙': [], '玄武': [], '白虎': [], '朱雀': []}
>>> for v in dict3.values(): print(id(v))
1533564587528
1533564587528
1533564587528
1533564587528
>>>
其实道理很简单,上述过程中,4个键的值(列表)所指向的一直是内存中的同一块区域,就像公用的房间,谁装修啊拆墙啊效果大家都看得见~
这个有点类似浅拷贝(传送门 -> Python 直接赋值、浅拷贝和深度拷贝解析
https://www.runoob.com/w3cnote/python-understanding-dict-copy-shallow-or-deep.html)
所以这时如果直接改变某个键的值,而非给值(列表)中的元素赋值,就不会有这种副作用。如下,改变 '玄武' 的值没有影响到别人,因为其值指向了别处,可以看到它的内存位置更新了,也就是它搬家啦,它在自己的新家里购置家具,曾经的室友当然看不见咯~
>>> dict3['玄武']=['斗', '牛']
>>> dict3
{'青龙': [], '玄武': ['斗', '牛'], '白虎': [], '朱雀': []}
>>> for v in dict3.values(): print(id(v))
1533564587528
1533564586888
1533564587528
1533564587528
>>>
4.那有什么应对措施吗?
首先想到的就是深拷贝,但是这种方法行不通的,因为地址虽然是新的,但是还是‘玄武’一个房间,其它三人一个房间。上文提到的“类似浅拷贝”是指,这4个列表之间的关系类似浅拷贝,把整个字典深拷贝是不能解决这个问题的。
>>> import copy
>>> dict4=copy.deepcopy(dict3)
>>> for v in dict4.values(): print(id(v))
1533564585992
1533561287368
1533564585992
1533564585992
>>>
害,其实这样创建字典不也挺麻烦吗?还不如像开头那样直接创建字典 dict_28star并同时赋值,或者像1或2那样给键的值直接赋值,省事、快捷、不留坑。
或者用个循环咯,四个人一人一屋,互不影响
>>> seq
('青龙', '玄武', '白虎', '朱雀')
>>> dict5={key: [] for key in seq}
>>> for v in dict5.values(): print(id(v))
1533564597192
1533564597000
1533564596744
1533564331720
>>>
所以目前看来 dict.fromkeys() 应该是适合那些字典中的值要保持一致的情况吧。比如吃火锅,大家点的东西都会在锅里或者即将在锅里哈哈哈~
更多推荐



所有评论(0)