关于python:collections.chainmap的目的是什么?

What is the purpose of collections.ChainMap?

在python 3.3中,ChainMap类被添加到collections模块中:

A ChainMap class is provided for quickly linking a number of mappings
so they can be treated as a single unit. It is often much faster than
creating a new dictionary and running multiple update() calls.

例子:

1
2
3
4
5
6
7
8
9
>>> from collections import ChainMap
>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = ChainMap(y, x)
>>> for k, v in z.items():
        print(k, v)
a 1
c 11
b 10

它是受这个问题的推动,并由这个问题公开的(没有创建PEP)。

据我所知,它是另一种选择,而不是拥有一个额外的字典,并用update()s维护它。

问题是:

  • ChainMap包含哪些用例?
  • 有没有关于ChainMap的真实例子?
  • 是否在第三方图书馆中使用切换到python3?

额外的问题:有没有办法在python2.x上使用它?

我在Raymond Hettinger的Transforming Code into Beautiful, Idiomatic PythonPycon Talk中听说过,我想把它添加到我的工具包中,但是我不知道什么时候应该使用它。


我喜欢@b4hand的例子,事实上,我在过去的链表结构(但不是链表本身)中使用了两个目的:多层配置覆盖和变量堆栈/作用域仿真。

我想指出与使用dict更新循环相比,ChainMap的另外两个动机/优势/差异,因此只存储"最终"版本:

  • 更多信息:由于链图结构是"分层的",它支持回答这样的问题:我是得到"默认"值还是被覆盖的值?原始("默认")值是多少?值在什么级别被覆盖(借用@b4hand的配置示例:用户配置或命令行覆盖)?使用简单的听写,回答这些问题所需的信息已经丢失。

  • 速度权衡:假设您在每个层中都有N层,并且最多有M个键,那么构造一个链映射就需要O(N)和每个查找O(N)最坏情况[*],而使用更新循环构造dict就需要O(NM)和每个查找O(1)。这意味着,如果您经常构建并且每次只执行几个查找,或者如果M很大,那么chainmap的惰性构建方法对您有利。

  • (2)中的分析假设dict访问平均为O(1),而实际上是O(1)O(M)的最坏情况。请在此处查看更多详细信息。


    我可以看到对配置对象使用ChainMap,其中您有多个配置范围,如命令行选项、用户配置文件和系统配置文件。由于查找是按constructor参数中的顺序排序的,因此可以覆盖较低范围的设置。我个人没有使用过或见过ChainMap使用过,但这并不奇怪,因为它是标准库中最近添加的一个。

    如果您试图自己实现一个词法范围,那么它也可能对模拟在推送和弹出变量绑定处的堆栈帧很有用。

    链图的标准库文档提供了几个例子,并链接到第三方库中的类似实现。具体地说,它命名Django的Context类和Enthund的MultiContext类。


    我来试试这个:

    链图看起来是一种非常简单的抽象。对于一个非常专业的问题来说,这是一个很好的解决方案。我提出这个用例。

    如果你有:

  • 多个映射(例如,dict)
  • 这些映射中的某些键重复(同一个键可以出现在多个映射中,但并非所有键都出现在所有映射中)
  • 希望访问"最高优先级"映射中某个键的值的消费应用程序,其中任何给定键的所有映射都有一个总的顺序(即,映射可以具有相同的优先级,但前提是已知这些映射中没有键的重复)(在Python应用程序中,包可以位于同一目录(同一优先级),但必须具有不同的名称,因此,根据定义,该目录中的符号名称不能重复。)
  • 使用应用程序不需要更改键的值
  • 同时,映射必须保持其独立的标识,并且可以通过外力异步更改。
  • 映射足够大,足够昂贵,可以访问,或者在应用程序访问之间经常更改,因此每次应用程序需要时计算投影(3)的成本是应用程序的一个重要性能问题…
  • 然后,您可以考虑使用链映射在映射集合上创建视图。

    但这一切都是事后证明。python的人遇到了一个问题,在他们的代码环境中提出了一个很好的解决方案,然后做了一些额外的工作来抽象他们的解决方案,这样我们可以在选择的时候使用它。给他们更多的力量。但这是否适合你的问题,由你来决定。


    不完全回答你的:

    Bonus question: is there a way to use it on Python2.x?

    1
    from ConfigParser import _Chainmap as ChainMap

    但是要记住,这不是一个真实的ChainMap,它继承了DictMixin,并且只定义了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    __init__(self, *maps)
    __getitem__(self, key)
    keys(self)

    # And from DictMixin:
    __iter__(self)
    has_key(self, key)
    __contains__(self, key)
    iteritems(self)
    iterkeys(self)
    itervalues(self)
    values(self)
    items(self)
    clear(self)
    setdefault(self, key, default=None)
    pop(self, key, *args)
    popitem(self)
    update(self, other=None, **kwargs)
    get(self, key, default=None)
    __repr__(self)
    __cmp__(self, other)
    __len__(self)

    它的实现似乎也不是特别有效。