collections.ChainMap 的目的是啥?

Posted

技术标签:

【中文标题】collections.ChainMap 的目的是啥?【英文标题】:What is the purpose of collections.ChainMap?collections.ChainMap 的目的是什么? 【发布时间】:2014-06-17 01:29:45 【问题描述】:

在 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()s 维护它的替代方法。

问题是:

ChainMap 涵盖哪些用例? 有ChainMap 的真实例子吗? 是否在切换到python3的第三方库中使用?

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


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

【问题讨论】:

实际用例:Web 框架中的 GET 和 POST 参数映射,提供两个不同且独立的字典的组合视图。 至于在 2.x 中使用它,source code 看起来可能只是工作,虽然我没有尝试过 FWIW,Python2.7 中已经有一个先驱:from ConfigParser import _ChainMap as ChainMap @RaymondHettinger 感谢您提供的漂亮实用的 cmets 以及 ChainMap 本身。你本可以从 cmets 链中得到一个很好的答案:) 如果你想像Raymond建议的那样在2.7中导入,它实际上是_Chainmap(注意m的大写) 【参考方案1】:

我将对此进行破解:

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

如果你有:

    多个映射(例如,dicts) 这些映射中的某些键重复(同一个键可以出现在多个映射中,但并非所有键都出现在所有映射中) 希望访问“最高优先级”映射中键的值的消费应用程序,其中对任何给定键的所有映射都有总排序(也就是说,映射可能具有相同的优先级,但前提是它已知这些映射中没有重复的键)(在 Python 应用程序中,包可以存在于同一目录中(相同优先级)但必须具有不同的名称,因此,根据定义,该目录中的符号名称不能重复.) 消费应用程序不需要更改键的值 同时映射必须保持其独立身份,并且可以通过外力异步更改 并且映射足够大、足够昂贵以访问或在应用程序访问之间经常更改,因此每次应用程序需要计算投影 (3) 的成本对您的应用程序来说是一个重要的性能问题...

那么, 您可能会考虑使用链图在映射集合上创建视图。

但这都是事后证明。 Python 人员遇到了一个问题,在他们的代码上下文中提出了一个很好的解决方案,然后做了一些额外的工作来抽象他们的解决方案,以便我们可以根据需要使用它。给他们更多的权力。但是否适合您的问题由您决定。

【讨论】:

Chainmap 在所有键都出现在所有映射中的情况下很有用,例如,如果您想推送和弹出映射的版本。 很公平,但如果你所有的字典都有相同的键,你可能可以更快地创建一个更新值的新字典。 是的,它会更快,但它不支持弹出/推送功能。【参考方案2】:

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

如果您尝试自己实现词法范围,它也可能对模拟堆栈帧很有用,您可以在其中推送和弹出变量绑定。

standard library docs for ChainMap 提供了几个示例和第三方库中类似实现的链接。具体来说,它命名为 Django 的 Context class 和 Enthought 的 MultiContext class。

【讨论】:

+1 这是一个很好的答案,涵盖了替代实现以及文档中提到的多个链式命名空间示例的链接。【参考方案3】:

我喜欢@b4hand 的示例,事实上我在过去使用过类似 ChainMap 的结构(但不是 ChainMap 本身)用于他提到的两个目的:多层配置覆盖和变量堆栈/范围仿真。

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

    更多信息: 由于 ChainMap 结构是“分层的”,因此它支持回答以下问题:我得到的是“默认”值还是被覆盖的值?原始(“默认”)值是多少?该值在什么级别被覆盖(借用@b4hand 的配置示例:user-config 或 command-line-overrides)?使用简单的 dict,回答这些问题所需的信息已经丢失。

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

[*] (2) 中的分析假设 dict-access 是O(1),而实际上它平均是O(1),最坏的情况是O(M)。查看更多详情here。

【讨论】:

+1 这是一个合理的比较。与其他技术有一些类比会很好。例如,操作系统命令行有一个路径的概念,它本质上是一个目录查找链,直到找到匹配项。在 Python 中,这将使用 ChainMap 建模。【参考方案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)

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

【讨论】:

以上是关于collections.ChainMap 的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Python中 collections模块的详细用法介绍

网络安全课程目的

优化的目的是啥

高德导航搜索目的地时,随着目的地的出现,出现了包括目的地在内的很多橙色的气泡是表示啥意思?

仓库名称变更目的是啥

Docker,它是啥,目的是啥