0

0

使用 ChainMap 实现 Python 字典的深度合并

心靈之曲

心靈之曲

发布时间:2025-08-11 18:16:41

|

355人浏览过

|

来源于php中文网

原创

使用 chainmap 实现 python 字典的深度合并

本文深入探讨了如何利用 Python 的 collections.ChainMap 实现复杂字典的深度合并。针对 ChainMap 默认的浅层合并行为无法满足嵌套字典合并的需求,文章提出了一种自定义 DeepChainMap 类的方法。通过重写 __getitem__ 方法,该方案能够递归地合并具有相同键的嵌套字典,从而实现真正的深度合并,同时保留 ChainMap 的惰性查找特性。

理解 ChainMap 的基本行为与局限性

collections.ChainMap 是 Python 标准库中一个非常实用的工具,它将多个字典或映射组合成一个单一的、可更新的视图。当查找一个键时,ChainMap 会按照传入字典的顺序依次查找,返回第一个找到的键对应的值。这在需要按优先级合并配置或环境变量等场景下非常有用。

考虑以下两个字典 a 和 b:

a = {'123': {'player': 1,
             'opponent': 2},
     '18': {'player': 10,
            'opponent': 12}
    }

b = {'123': {'winner': 1},
     '180': {'winner': 2}
    }

如果直接使用 ChainMap(a, b) 并尝试将其转换为字典,我们会发现 ChainMap 默认的行为是浅层合并。对于相同的顶级键,它会优先使用第一个字典中的值,而不会尝试合并嵌套的字典:

from collections import ChainMap

print(dict(ChainMap(a, b)))
# 输出: {'123': {'player': 1, 'opponent': 2}, '180': {'winner': 2}, '18': {'player': 10, 'opponent': 12}}

可以看到,键 '123' 的值直接取自 a,b 中 '123' 下的 {'winner': 1} 被完全忽略了。这与我们期望的深度合并结果,即 '123' 下的字典应合并为 {'player': 1, 'opponent': 2, 'winner': 1},相去甚远。

立即学习Python免费学习笔记(深入)”;

实现深度合并:自定义 DeepChainMap

要实现对嵌套字典的深度合并,我们需要扩展 ChainMap 的行为,使其在遇到嵌套字典时能够递归地进行合并。这可以通过重写 DeepChainMap 类的 __getitem__ 方法来实现。当 ChainMap 尝试获取一个键的值时,如果该值本身是一个字典,我们则递归地为这个嵌套字典创建另一个 DeepChainMap 实例,从而实现深度合并。

Codiga
Codiga

可自定义的静态代码分析检测工具

下载

以下是 DeepChainMap 的实现:

from collections import ChainMap

class DeepChainMap(ChainMap):
    """
    ChainMap 的变体,支持对嵌套字典的深度合并。
    当获取一个键的值时,如果该值是字典,则递归地合并所有映射中该键对应的字典。
    """
    def __getitem__(self, key):
        # 收集所有映射中存在给定键的值
        values = (mapping[key] for mapping in self.maps if key in mapping)

        try:
            # 获取第一个找到的值
            first = next(values)
        except StopIteration:
            # 如果所有映射中都不存在该键,则调用父类的 __missing__ 方法(通常会抛出 KeyError)
            return self.__missing__(key)

        # 如果第一个值是字典,则递归地为所有找到的字典值创建 DeepChainMap
        if isinstance(first, dict):
            # 注意:这里将 first 放在第一个,以确保其内容优先,
            # 并且后续的 values 会按 ChainMap 的顺序进行合并
            return self.__class__(first, *values)

        # 如果不是字典,直接返回第一个找到的值
        return first

    def __repr__(self):
        # 为了更清晰的表示,将 DeepChainMap 转换为普通字典的字符串表示
        return repr(dict(self))

__getitem__ 方法解析

  1. values = (mapping[key] for mapping in self.maps if key in mapping): 这一行是核心。它遍历 DeepChainMap 内部存储的所有映射(即传入的原始字典),并生成一个迭代器,其中包含所有找到的、与给定 key 关联的值。这样,即使 key 在多个字典中都存在,我们也能获取到它们所有的值。
  2. first = next(values): 尝试从 values 迭代器中获取第一个值。这个值是根据 ChainMap 的查找顺序(即传入字典的顺序)找到的第一个值。
  3. except StopIteration: return self.__missing__(key): 如果 values 迭代器为空(即在任何映射中都没有找到 key),则抛出 StopIteration 异常。我们捕获这个异常并调用 self.__missing__(key),这通常会导致 KeyError,符合字典找不到键的行为。
  4. *`if isinstance(first, dict): return self.class(first, values)**: 这是实现深度合并的关键。如果first值是一个字典,这意味着我们遇到了一个需要深度合并的嵌套结构。此时,我们递归地创建一个新的DeepChainMap实例。这个新的DeepChainMap将以first字典作为其第一个映射,然后将values迭代器中剩余的所有字典(即在其他映射中找到的、与相同key关联的字典)作为后续映射传入。这样,新的DeepChainMap` 会负责合并这些嵌套字典。
  5. return first: 如果 first 值不是字典(例如,它是字符串、数字、列表等),则不需要深度合并,直接返回 first 即可。

__repr__ 方法解析

__repr__ 方法被重写是为了提供一个更友好的字符串表示。默认情况下,ChainMap 的 repr 会显示其内部的 maps 列表,这对于调试和理解 DeepChainMap 的最终状态不太直观。通过将其转换为 dict(self) 再进行 repr,我们可以直接看到合并后的字典结构,就像一个普通的字典一样。

使用 DeepChainMap 进行深度合并

现在,我们可以使用自定义的 DeepChainMap 来实现我们最初的目标:

# 重新定义原始字典
a = {'123': {'player': 1,
             'opponent': 2},
     '18': {'player': 10,
            'opponent': 12}
    }

b = {'123': {'winner': 1},
     '180': {'winner': 2}
    }

# 使用 DeepChainMap 进行合并
merged_map = DeepChainMap(a, b)
print(merged_map)
# 预期输出: {'123': {'winner': 1, 'player': 1, 'opponent': 2}, '180': {'winner': 2}, '18': {'player': 10, 'opponent': 12}}

# 转换为普通字典以进行进一步操作(如果需要)
final_dict = dict(merged_map)
print(final_dict)
# 输出: {'123': {'winner': 1, 'player': 1, 'opponent': 2}, '180': {'winner': 2}, '18': {'player': 10, 'opponent': 12}}

从输出可以看出,对于键 '123',嵌套字典 { 'player': 1, 'opponent': 2 } 和 { 'winner': 1 } 已经被成功合并为一个完整的字典 { 'player': 1, 'opponent': 2, 'winner': 1 }。同时,非重叠的键 '18' 和 '180' 也被正确保留。

注意事项与总结

  • 惰性查找特性: DeepChainMap 继承了 ChainMap 的惰性查找特性。这意味着只有当实际访问某个键时,才会执行查找和潜在的深度合并操作。这对于处理非常大的字典集合,但只访问其中一部分数据的情况,可以提高效率。
  • 合并顺序: 合并的优先级仍然遵循 ChainMap 的规则,即传入 DeepChainMap 的字典顺序。对于非字典类型的值,第一个找到的值会被返回。对于字典类型的值,它们会递归地合并,但后续字典中的同名键会覆盖前面字典中的同名键(这是 dict.update() 或字典合并的常见行为)。
  • 适用场景: DeepChainMap 特别适用于需要将多个配置源(如默认配置、用户配置、命令行参数)进行深度合并的场景,尤其当这些配置中包含嵌套结构时。
  • 与递归更新的区别: 如果你仅仅需要一个最终的、完全合并后的新字典,并且不关心 ChainMap 的惰性查找特性,那么编写一个递归函数来更新字典可能是另一种更直接的方式。然而,DeepChainMap 提供了一种优雅且面向对象的方式来处理这类深度合并问题,尤其是在需要维护多个字典视图的场景下。

通过自定义 DeepChainMap,我们成功扩展了 collections.ChainMap 的功能,使其能够处理更复杂的深度合并需求,为 Python 开发者提供了更强大的字典操作工具。

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

753

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

636

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

758

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

618

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1262

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

547

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

577

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

707

2023.08.11

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

36

2026.01.14

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.7万人学习

Django 教程
Django 教程

共28课时 | 3.1万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号