在Python 3.3中,ChainMap
模块中添加了collections
类:
提供了一个ChainMap类,用于快速链接多个映射 所以它们可以被视为一个单元。它通常比快得多 创建一个新字典并运行多个update()调用。
示例:
>>> 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
由this issue推动,并由this one公开(未创建PEP
)。
据我了解,它可以替代使用额外字典并使用update()
进行维护。
问题是:
ChainMap
涵盖哪些用例?ChainMap
的真实世界示例? 奖金问题:有没有办法在Python2.x上使用它?
我在Raymond Hettinger的Transforming Code into Beautiful, Idiomatic Python
PyCon演讲中听说过它,我想将它添加到我的工具包中,但我不知道何时应该使用它。
答案 0 :(得分:53)
我喜欢@ b4hand的例子,实际上我在过去使用类似ChainMap的结构(但不是ChainMap本身)中使用了他提到的两个目的:多层配置覆盖和变量堆栈/范围仿真
与使用dict-update循环相比,我想指出ChainMap
的另外两个动机/优点/差异,因此只存储" final"版本":
更多信息:,因为ChainMap结构是"分层",它支持回答问题:我是否得到"默认"价值,或被覆盖的?什么是原始("默认")值?在什么级别上覆盖了值(借用@ b4hand&#39的配置示例:user-config或命令行覆盖)?使用简单的词典,回答这些问题所需的信息已经丢失。
速度权衡:假设每个都有N
个图层和最多M
个密钥,构建一个ChainMap需要O(N)
和每个查找{ {1}}最坏情况[*],而使用更新循环构造dict需要O(N)
和每个查找O(NM)
。这意味着如果您经常构建并且每次只执行一些查找,或者O(1)
很大,ChainMap的惰性构造方法对您有利。
[*](2)中的分析假设dict-access为M
,实际上平均为O(1)
,O(1)
最差。查看更多详情here。
答案 1 :(得分:32)
我可以看到使用ChainMap
作为配置对象,其中有多个配置范围,如命令行选项,用户配置文件和系统配置文件。由于查找按构造函数参数中的顺序排序,因此您可以覆盖较低范围的设置。我没有亲自使用或看过ChainMap
,但这并不奇怪,因为它是标准库的最新版本。
如果你试图自己实现一个词法范围,那么在你推送和弹出变量绑定的情况下模拟堆栈帧也可能很有用。
standard library docs for ChainMap给出了几个示例以及第三方库中类似实现的链接。具体来说,它命名为Django的Context class和Enthought的MultiContext class。
答案 2 :(得分:6)
我会对此采取行动:
Chainmap看起来像一种非常恰当的抽象。对于一个非常特殊的问题,它是一个很好的解决方案。我提出这个用例。
如果你有:
然后, 您可以考虑使用链图来创建映射集合的视图。
但这都是事后的理由。 Python人员遇到了问题,在他们的代码环境中提出了一个很好的解决方案,然后做了一些额外的工作来抽象他们的解决方案,以便我们可以选择使用它。给他们更多的力量。但是,它是否适合您的问题由您决定。
答案 3 :(得分:4)
不完美地回答你:
奖金问题:有没有办法在Python2.x上使用它?
from ConfigParser import _Chainmap as ChainMap
但请注意,这不是真正的ChainMap
,它继承自DictMixin
并且只定义:
__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)
它的实施似乎也不是特别有效。