1.20 合并多个字典或映射

有多个字典或映射 将他们从逻辑上合并为一个单一的映射后执行某些操作 比如 查找值或检查某些键是否存在


In [1]:
a = {'x': 1, 'z': 3}
b = {'y': 2, 'z': 4}
# 需在两 dict 中执行查找操作 (先从 a 中找,若是找不到,再在 b 中找)
from collections import ChainMap
c = ChainMap(a,b)
print(c['x'])
print(c['y'])
print(c['z'])


1
2
3

一个 ChainMap 接受多个 dict 将他们在逻辑上变为一个 dict 然后 这些 dict 不是真的合并在一起了 ChainMap 类只是在内部创建了一个容纳这些 dict 的 list and 重新定义了一些常用的 dict 操作来遍历这些列表 大部分 dict 都是正常使用的


In [3]:
len(c)


Out[3]:
3

In [4]:
list(c.keys())


Out[4]:
['z', 'y', 'x']

In [5]:
list(c.values())


Out[5]:
[3, 2, 1]

if 出现重复键 即 其中 'z' 那么以第一次出现的映射值为主 同时被返回 SO 其中 c['z'] 总是返回 dict a 中 对应的值 而不是 b 中的值
对于 dict 的更新或删除操作总是影响的列表中的 第一个 dict


In [12]:
c['z'] = 10
c['w'] = 80
del c['x']


---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
D:\Anaconda3\lib\collections\__init__.py in __delitem__(self, key)
    911         try:
--> 912             del self.maps[0][key]
    913         except KeyError:

KeyError: 'x'

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
<ipython-input-12-10ff365cc705> in <module>()
      1 c['z'] = 10
      2 c['w'] = 80
----> 3 del c['x']

D:\Anaconda3\lib\collections\__init__.py in __delitem__(self, key)
    912             del self.maps[0][key]
    913         except KeyError:
--> 914             raise KeyError('Key not found in the first mapping: {!r}'.format(key))
    915 
    916     def popitem(self):

KeyError: "Key not found in the first mapping: 'x'"

知道为啥以上 key 他妈都找不到了,并未按顺序进行 这里的顺序是指 Time Line 按时间先后执行


In [7]:
c_old = ChainMap(a,b)

In [8]:
c_old


Out[8]:
ChainMap({'z': 10, 'w': 80}, {'y': 2, 'z': 4})

In [ ]:


In [9]:
type(c_old)


Out[9]:
collections.ChainMap

ChainMap 对于语言中 variable 作用范围 (globals , locals)非常有用 will make something easy


In [13]:
values = ChainMap()

values['x'] = 1
# Add a new mapping
values = values.new_child()
values['x'] = 2
# Add a new mapping
values = values.new_child()
values['x'] = 3

In [14]:
values


Out[14]:
ChainMap({'x': 3}, {'x': 2}, {'x': 1})

In [15]:
values['x']


Out[15]:
3

In [16]:
# Discard last mapping
values = values.parents
values['x']


Out[16]:
2

In [17]:
# Discard last mapping
values = values.parents
values['x']


Out[17]:
1

In [18]:
values


Out[18]:
ChainMap({'x': 1})

作为 ChainMap 的替代 考虑使用 update 将两个 dict 合并


In [22]:
a = {'x':1,'z':3}
b = {'y':2,'z':4}
merged = dict(b)
merged.update(a)

In [23]:
print(merged['x'],'\n\n',merged['y'],'\n\n',merged['z'])


1 

 2 

 3

虽然以上可行 但需要创建一个完全不同的 dict 对象 (可能破坏现有 dict 结构) 同时 若 原 dict 做了更新 这种更新不会反映到 新的合并 dict 中去


In [25]:
a['x']  = 19
merged['x']


Out[25]:
1

ChainMap 使用原来 dict 他自己不会创建 new dict SO 其 会影响其本身


In [26]:
a = {'x': 1, 'z': 3}
b = {'y': 2, 'z': 4}
merged = ChainMap(a,b)

In [27]:
merged['x']


Out[27]:
1

In [28]:
a['x'] = 43
merged['x'] # Notice change to merged dict


Out[28]:
43