Python collections模块-ChainMap

ChainMap简介:

ChainMap可以用来连接多个字典,进行顺序搜索,达到与合并字典类似的功能

我将会从增删改查,以及ChainMap的方法来分析它的一些工作方式

  • 查找

ChainMap查找时,会依赖初始化时参数的顺序

from collections import ChainMap

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 'B', 'c': 'C'}

chained = ChainMap(dict1, dict2)
merged = {**dict1, **dict2}

# 字典连接之后,在查找时会按照初始化时给定的顺序对内部的字典进行搜索
# 所以chained['b']的值是2
assert chained['b'] == 2

# 字典合并时,如果初始化参数中含有相同的key,
# 如上面dict1与dict2中的'b'作为key是冲突的,
# 那么最终的结果由参数中靠后的值决定。
assert merged['b'] == 'B'

# 在key没有冲突的时候,字典连接与字典合并,在查找时得到的结果是没有区别的。
assert chained['a'] == merged['a']
assert chained['c'] == merged['c']

# 在key有冲突时,如果想要字典合并与字典连接在查找时
# 没有区别,那么我们应该反转两者初始化时参数的顺序

# merged1初始化时的参数顺序与chained相反
merge1 = {**dict2, **dict1}
assert merged1['b'] == chained['b']

# chained1初始化时的参数顺序与merged相反
chained1 = ChainMap(dict2, dict1)
assert merged['b'] == chained1['b']
  • 更新

ChainMap在更新时,连接中第一个字典。连接的顺序可以通过ChainMap.maps来查看

# 可以通过 .maps方法,来查看连接的顺序。
print(chained.maps)
# [{'a': 1, 'b': 2}, {'b': 'B', 'c': 'C'}]

chained['b'] = 3
print(chained)
# ChainMap({'a': 1, 'b': 3}, {'b': 'B', 'c': 'C'})
# 第一个字典中'b'的映射变成了'3'
  • 删除

删除的时候与更新类似,只能操作连接中第一个字典

# 删除连接中的 'b'
del chained['b']
print(chained)
# ChainMap({'a': 1}, {'b': 'B', 'c': 'C'})
# 此时第一个字典中的 'b' 被移除了。

# 此时连接中,第二个字典还包含了 'b'
del chained['b']
print(chained)
# 再尝试进行删除的时候报错了,删除只能在连接的第一个字典进行操作。
# >>> del chained['b']
# Traceback (most recent call last):
#   File "/usr/lib/python3.8/collections/__init__.py", line 951, in __delitem__
#     del self.maps[0][key]
# KeyError: 'b'
  • 新增

新增与删除和更新一样,只会对连接中第一个字典进行操作,不会影响到其他字典的值

chained['d'] = 'D'
print(chained)
# ChainMap({'a': 1, 'd': 'D'}, {'b': 'B', 'c': 'C'})
# 第一个字典中出现了'd'->'D' 这个键值对

chained['c'] = 'C1'
print(chained)
# ChainMap({'a': 1, 'd': 'D', 'c': 'C1'}, {'b': 'B', 'c': 'C'})
# 第一个字典出现了'c' -> 'C1'这个键值对,
# 而没有影响到已经存在'c' -> 'C'的第二个字典
  • 遍历
for k, v in chained.items():
	print(k, v)
# b B
# c C1
# a 1
# d D
# 即使连接中的字典有重复的key,但是所有的键只会输出一次

通过上面的对比,我们现在应该知道了:

  • ChainMap可以让我们在不合并字典的情况下,在多个字典中寻找值,避免了字典合并,需要对key重新哈希带来的损耗
  • 在进行修改操作时,ChainMap限制了操作只会对连接中第一个字典生效

  • maps方法
print(chained.maps)
# [{'a': 1, 'b': 2}, {'b': 'B', 'c': 'C'}]
# maps方法可以看到连接内部的顺序,也就是连接内部结构。
# 所以我们可以知道,ChainMap内部就是由列表与字典构成。
  • new_child方法
# new_child方法可以不传参数或者传入一个字典。
# 不传参数相当于传入一个空字典
# 同时这个方法会返回一个新的ChainMap
# 这个方法,会将传入的字典,放到新ChainMap连接的第一位
# 即后续的修改都会操作这个传入的字典

print(chained.new_child())
# ChainMap({}, {'a': 1, 'b': 2}, {'b': 'B', 'c': 'C'})

print(chained.new_child({'a': 3, 'b': 4}))
# ChainMap({'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {'b': 'B', 'c': 'C'})
  • parents属性
# ChainMap的parents属性还是一个ChainMap

# parents这个ChainMap内部的连接移除了当前ChainMap连接中的第一个字典

print(chained.parents)
# ChainMap({'b': 'B', 'c': 'C'})

print(id(chained.parents == chained.parents))
# True

print(id(chained.new_child().parents) == chained)
# False
  • ChainMap的其他方法与字典类似,不再作详细的介绍
# 复制
chained.copy()

# 以下修改的操作,均只会对连接中第一个字典生效。

# 清空,ChainMap的连接变为空,且连接中的第一个字典也会清空,不会影响后续的字典
chained.clear()
chained.pop()
chained.popitem()
.....
展示评论