说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 的 collections 内置模块提供的 ChainMap 的主要功能是将多个字典或者其他其他映射(mappings)类型数据组合在一起,形成一个单一的可更新的视图。最典型的场景是我们有多个字典,想把这些字典合并成一个单独的字典,就可以利用 ChainMap() 方法。这样的操作并不是对源数据的拷贝,而是指向源数据,假如原字典数据修改,ChainMap 映射也会改变;如果对 ChainMap 的结果修改,那么原数据一样也会被修改,这样就不会造成数据不同步的问题,当然需要因使用场景而定。
ChainMap 操作的是一些映射(mappings)类型数据,那么如何理解映射数据呢?简单说,可以通过名字来引用值的数据结构称为映射,比如字典就是典型的映射,它可以通过 key 得到 value。
一个 mapping 对象将可哈希(hashable [1])的值映射为任意对象。映射是可变对象。目前 Python 中只有一种标准映射类型,就是字典(dict)。dcit 和 Set 集合一样也是用花括号表示,但是花括号中的每个元素都是一个键值对 (key:value)
。字典中的键值对也是无序的,且 key 必须是可哈希的不可变类型,如字符串、数字、布尔值和不包含可变类型的 tuple。而 list 和包含可变类型的 tuple 是不能做字典的 key 的。另外,同一个字典中,key 不能重复,否则会覆盖之前的值。
用于键的数字类型遵守数字比较的正常规则:如果两个数字比较相等(比如 1 和 1.0),则它们可以互相使用以索引相同的字典条目。但是需要注意,由于计算机存储浮点数作为近似值,因此使用它们作为字典的键通常是不明智的。
[1] 可哈希(hashable)是什么意思?首先了理解啥叫哈希(hash),可通过本教程的相关内容先进行了解。
以下是基本的创建和使用方法:
from collections import ChainMap
ChainMap()
# ChainMap({})
dict1 = dict(a=1, b=2)
dict2 = dict(b=3, c=4)
cm = ChainMap(dict1, dict2)
cm
# ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4})
type(cm)
# collections.ChainMap
cm.get('a') # 1
cm.get('b') # 2
cm.maps
# [{'a': 1, 'b': 2}, {'b': 3, 'c': 4}]
cm.maps[0]
# {'a': 1, 'b': 2}
cm.maps[1]['b']
cm.maps[1].get('b') # 同上
# 3
cm.items()
# ItemsView(ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4}))
cm.values()
# ValuesView(ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4}))
for i in cm.items():
print(i)
'''
('b', 2)
('c', 4)
('a', 1)
'''
for i in cm.values():
print(i)
'''
2
4
1
'''
接下来对它进行修改:
# 修改 b 值
cm['b'] = 12
cm
# ChainMap({'a': 1, 'b': 12}, {'b': 13, 'c': 4})
# ChainMap({'a': 1, 'b': 12}, {'b': 13, 'c': 4})
dict1
# {'a': 1, 'b': 12}
dict2
# {'b': 13, 'c': 4}
# 修改第二个字典 b 值
cm.maps[1]['b'] = 13
cm
# ChainMap({'a': 1, 'b': 2}, {'b': 13, 'c': 4})
dict2
# {'b': 13, 'c': 4}
ChainMap() 有以下特点:
从原理上面讲,ChainMap 实际上是把放入的字典存储在一个队列中,当进行字典的增加删除等操作只会在第一个字典上进行,当进行查找的时候会依次查找,new_child() 方法实质上是在列表的第一个元素前放入一个字典,默认是{},而 parents 是去掉了列表开头的元素。因为 ChainMap 底层是列表实现的,所以实际上 ChainMap 查询的字典实际上还是原来的字典的引用。
ChainMap() 支持所有常用字典方法。另外还有一个 maps 属性(attribute),一个创建子上下文的方法(method), 一个存取它们首个映射的属性(property):
一个可以更新的映射列表。这个列表是按照第一次搜索到最后一次搜索的顺序组织的。它是仅有的存储状态,可以被修改。列表最少包含一个映射。
from collections import ChainMap
dict1 = dict(a=1, b=2)
dict2 = dict(b=3, c=4)
cm = ChainMap(dict1, dict2)
cm
# ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4})
cm.maps
# [{'a': 1, 'b': 2}, {'b': 3, 'c': 4}]
cm.maps[0]['a'] = 0
cm.maps
# [{'a': 0, 'b': 2}, {'b': 3, 'c': 4}]
返回一个新的 ChainMap 类,包含了一个新映射(map),后面跟随当前实例的全部映射(map)。如果 m 被指定,它就成为不同新的实例,就是在所有映射前加上 m,如果没有指定,就加上一个空字典,这样的话一个 cm.new_child()
调用等价于 ChainMap({}, *cm.maps)
。这个方法用于创建子上下文,不改变任何父映射的值。
cm
# ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4})
cm.new_child()
# ChainMap({}, {'a': 0, 'b': 2}, {'b': 3, 'c': 4})
cm.new_child(dict(d=5))
# ChainMap({'d': 5}, {'a': 0, 'b': 2}, {'b': 3, 'c': 4})
parents 属性返回一个新的 ChainMap 包含所有的当前实例的映射,除了第一个。这样可以在搜索的时候跳过第一个映射。 使用的场景类似在 nested scopes 嵌套作用域中使用 nonlocal 关键词。用例也可以类比内建函数 super() 。一个 cm.parents
的引用等价于 ChainMap(*cm.maps[1:])
。
cm
# ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4})
cm.parents
# ChainMap({'b': 3, 'c': 4})
cm.new_child(dict(d=5)).parents
# ChainMap({'a': 0, 'b': 2}, {'b': 3, 'c': 4})
注意,一个 ChainMap() 的迭代顺序是通过从后往前扫描所有映射来确定的,这在我们开头的迭代操作中就可以看出来。
cm.new_child(dict(d=5))
# ChainMap({'d': 5}, {'a': 0, 'b': 2}, {'b': 3, 'c': 4})
for i in cm.new_child(dict(d=5)):
print(i)
'''
b
c
a
d
'''
更新时间:Oct. 3, 2021, 12:27 p.m. 标签:python 映射