You are here:  Home » Python » collections.ChainMap类详解 – (27)Python语言(必读进阶学习教程)(参考资料)

该模块实现了专门的容器数据类型,为 Python 的通用内置容器、dict、list、set 和 tuple 提供了替代方案。

提供了一个 ChainMap 类用于快速链接多个映射,因此它们可以被视为一个单元。 它通常比创建新字典和运行多个 update() 调用要快得多。

该类可用于模拟嵌套范围,并且在模板中很有用。

class collections.ChainMap(*maps)
ChainMap 将多个 dicts 或其他映射组合在一起以创建一个可更新的视图。 如果未指定映射,则提供一个空字典,以便新链始终具有至少一个映射。

底层映射存储在列表中。 该列表是公开的,可以使用 maps 属性访问或更新。 没有其他状态。

查找连续搜索底层映射,直到找到一个键。 相反,写入、更新和删除仅对第一个映射进行操作。

ChainMap 通过引用合并底层映射。 因此,如果其中一个底层映射得到更新,这些更改将反映在 ChainMap 中。

支持所有常用的字典方法。 此外,还有一个 maps 属性、一个用于创建新子上下文的方法,以及一个用于访问除第一个映射之外的所有内容的属性:

maps
用户可更新的映射列表。该列表从首次搜索到最后搜索排序。它是唯一存储的状态,可以进行修改以更改搜索的映射。该列表应始终包含至少一个映射。
new_child(m=None)
返回一个新的 ChainMap,其中包含一个新地图,后跟当前实例中的所有地图。 如果指定了 m,则它成为映射列表前面的新映射; 如果未指定,则使用空字典,因此对 d.new_child() 的调用等效于:ChainMap({}, *d.maps)。 此方法用于创建可以在不更改任何父映射中的值的情况下更新的子上下文。

在 3.4 版更改: 添加了可选的 m 参数。

parents
属性返回一个新的 ChainMap,其中包含当前实例中除第一个之外的所有地图。 这对于跳过搜索中的第一张地图很有用。 用例类似于嵌套作用域中使用的 nonlocal 关键字。 这些用例也与内置 super() 函数的用例平行。 对 d.parents 的引用等价于:ChainMap(*d.maps[1:])。

请注意,ChainMap() 的迭代顺序是通过从最后扫描到第一个映射来确定的:

>>> baseline = {'music': 'bach', 'art': 'rembrandt'}
>>> adjustments = {'art': 'van gogh', 'opera': 'carmen'}
>>> list(ChainMap(adjustments, baseline))
['music', 'art', 'opera']

这给出了与从最后一个映射开始的一系列 dict.update() 调用相同的顺序:

>>> combined = baseline.copy()
>>> combined.update(adjustments)
>>> list(combined)
['music', 'art', 'opera']

也可以看看

  • Enthought CodeTools包中的MultiContext类具有支持写入链中任何映射的选项。
  • Django 用于模板化的Context类是一个只读的映射链。它还具有类似于new_child()方法和 parents属性的上下文推送和弹出功能 。
  • 所述嵌套上下文配方具有选项来控制写入和其它突变是否只适用于第一映射或链中的任何映射。
  • 一个极为简化的Chainmap只读版本。

ChainMap例子

本节展示了使用链式地图的各种方法。

模拟 Python 内部查找链的示例:

import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))

让用户指定的命令行参数优先于环境变量的示例,而环境变量又优先于默认值:

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'])

使用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

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'})