ChainMap类用于快速链接多个映射,以便将它们视为一个单元。它通常比创建新字典和多次调用update()快得多。
该类可用于模拟嵌套作用域,在模板中很有用。
class collections.ChainMap(*maps)
ChainMap将多个字典或其他映射组合在一起以创建单个可更新视图。如果未指定maps,则提供单个空字典,以便新链始终至少具有一个映射。
底层映射存储在列表中。该列表是公共的,可以使用maps属性访问或更新。没有其他声明。
查找会连续搜索映射,直到找到key。相反,写入,更新和删除仅在第一个映射上运行。
ChainMap通过引用合并了底层映射。因此,如果其中一个底层映射得到更新,那么这些更改将反映在ChainMap中。
译者实例:
注意d和e,都存储在ChainMap中,但是搜索的时候从左到右,先到先得。
def collection_test2():
import builtins
from collections import ChainMap
a = {"name": "leng"}
b = {"age": 24}
c = {"wife": "qian"}
pylookup = ChainMap(a,b,c)
print(pylookup)
print(pylookup['age'],pylookup.maps)
pylookup.update({"age": 25})
print(pylookup)
b['age'] = 26
print(pylookup)
print(type(pylookup.maps))
pylookup.maps[0]['age']=20
pylookup.maps[1]['age']=22
print(pylookup)
print("-----------")
d = {"name": "leng"}
e = {"name":"123"}
cm = ChainMap(d,e)
print(cm)
print(cm['name'])
collection_test2()
#输出结果
ChainMap({'name': 'leng'}, {'age': 24}, {'wife': 'qian'})
24 [{'name': 'leng'}, {'age': 24}, {'wife': 'qian'}]
ChainMap({'name': 'leng', 'age': 25}, {'age': 24}, {'wife': 'qian'})
ChainMap({'name': 'leng', 'age': 25}, {'age': 26}, {'wife': 'qian'})
<class 'list'>
ChainMap({'name': 'leng', 'age': 20}, {'age': 22}, {'wife': 'qian'})
-----------
ChainMap({'name': 'leng'}, {'name': '123'})
leng
支持所有常用的字典方法。此外,还有一个 maps属性,一个用于创建新子上下文的方法,以及一个用于访问除第一个映射之外的所有映射的属性:
maps
用户可更新的映射列表。该列表从首次搜索到最后搜索排序。它是唯一存储的状态,可以进行修改以更改搜索的映射。该列表应始终包含至少一个映射。
new_child(m=None)
返回包含新映射的ChainMap,后跟当前实例中的所有映射。如果指定m
,则它将成为映射列表最前面的新映射; 如果未指定,则使用空的dict。所以d.new_child()
等效于ChainMap({}, *d.maps)
。此方法用于创建可在不更改任何父映射中的值的情况下更新的子上下文。
版本3.4中已更改:添加了可选参数m
。
parents
返回新的ChainMap,包含当前实例中除第一个之外的所有映射。这对于在搜索中跳过第一个映射很有用。用例类似于嵌套作用域中使用的nonlocal关键字。用例也与内置函数super()的用法相同 。引用d.parents
相当于ChainMap(*d.maps[1:])
。
译者实例:
def collection_test3():
import builtins
from collections import ChainMap
a = {"name": "leng","age": 20}
b = {"age": 24}
c = {"wife": "qian"}
cm = ChainMap(a,b,c)
nc1 = cm.new_child()
nc2 = cm.new_child(m=b)
print(nc1,nc2,sep='\n')
print("_________")
print(nc2.parents,nc1.parents,sep='\n')
collection_test3()
#输出结果
ChainMap({}, {'name': 'leng', 'age': 20}, {'age': 24}, {'wife': 'qian'})
ChainMap({'age': 24}, {'name': 'leng', 'age': 20}, {'age': 24}, {'wife': 'qian'})
_________
ChainMap({'name': 'leng', 'age': 20}, {'age': 24}, {'wife': 'qian'})
ChainMap({'name': 'leng', 'age': 20}, {'age': 24}, {'wife': 'qian'})
也可以看看
Enthought的 CodeTools包中的MultiContext类具有支持写入链中任何映射的选项。
Django 用于模板化的Context类是一个只读的映射链。它还具有类似于new_child()方法和 parents属性的上下文推送和弹出功能 。
嵌套上下文方法具有选项来控制写入和其它突变是否只适用于第一映射或链中的任何映射。
一个大大简化的Chainmap只读版本。
实际用途
本节介绍使用ChainMap的各种方法。
(1)模拟Python内部查找链的示例:
import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))
(2)让用户指定的命令行参数优先于环境变量的示例,而环境变量优先于默认值:
import os, argparse
defaults = {'color': 'red', 'user': 'guest'}
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user')
parser.add_argument('-c', '--color')
namespace = parser.parse_args()
command_line_args = {k:v for k, v in vars(namespace).items() if v}
combined = ChainMap(command_line_args, os.environ, defaults)
print(combined['color'])
print(combined['user'])
(3)使用ChainMap类来模拟嵌套上下文的示例模式:
c = ChainMap() # Create root context
d = c.new_child() # Create nested child context
e = c.new_child() # Child of c, independent from d
e.maps[0] # Current context dictionary -- like Python's locals()
e.maps[-1] # Root context -- like Python's globals()
e.parents # Enclosing context chain -- like Python's nonlocals
d['x'] # Get first key in the chain of contexts
d['x'] = 1 # Set value in current context
del d['x'] # Delete from current context
list(d) # All nested values
k in d # Check all nested values
len(d) # Number of nested values
d.items() # All nested items
dict(d) # Flatten into a regular dictionary
(4)如果需要对ChainMap进行深度写入和删除,则很容易创建一个子类来更新链中更深层次的键:
class DeepChainMap(ChainMap):
'Variant of ChainMap that allows direct updates to inner scopes'
def __setitem__(self, key, value):
for mapping in self.maps:
if key in mapping:
mapping[key] = value
return
self.maps[0][key] = value
def __delitem__(self, key):
for mapping in self.maps:
if key in mapping:
del mapping[key]
return
raise KeyError(key)
>>> d = DeepChainMap({'zebra': 'black'}, {'elephant': 'blue'}, {'lion': 'yellow'})
>>> d['lion'] = 'orange' # update an existing key two levels down
>>> d['snake'] = 'red' # new keys get added to the topmost dict
>>> del d['elephant'] # remove an existing key one level down
>>> d # display result
DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'})
(5)当不同的字典具有相同的主键的时候,在遍历串联之后的数据时,会只能遍历到之前的
user_dict1 = {"a": "xiaohong", "b": "xiaohua"}
user_dict2 = {"b": "xiaopang", "d": "xiaoming"}
new_dict = ChainMap(user_dict1, user_dict2)
for key, value in new_dict.items():
print(key, value)
# d xiaoming
# b xiaohua
# a xiaohong