Python小酷库系列:Munch,用对象的访问方式访问dict


Munch 是 Python 中一个小巧实用的库,它将字典(dict)扩展为支持“点操作”的对象访问方式,即:

data = Munch({"name":"Alice", "age":18})
print(data.name)   # 而不是 data["name"]

它特别适用于配置读取、JSON 响应处理、动态数据访问等场景。

基本使用

1、创建一个 Munch 对象

from munch import Munch

m = Munch()
m.name = "Alice"
m.age = 30

print(m['name'])   # Alice
print(m.name)      # Alice

2、使用字典初始化

user = Munch({'id': 1, 'username': 'bob'})
print(user.username)  # bob

3、访问不存在的字段

当你访问一个 Munch 对象中不存在的键(属性) 时,Munch将抛出AttributeError。我们可以通过以下几种方式安全地访问Munch 对象的键(属性):
使用 .get() 方法(dict 风格)

print(m.get('age'))           # 输出 None
print(m.get('age', 0))        # 输出默认值 0

使用 in 判断键是否存在

if 'age' in m:
    print(m.age)

使用 DefaultMunch 自动返回默认值(推荐)

from munch import DefaultMunch

m = DefaultMunch(None, {"name": "Alice"})

print(m.name)   # Alice
print(m.age)    # None(不会报错)

4、嵌套结构支持

from munch import DefaultMunch
nested = DefaultMunch.fromDict({
    "user": {
        "id": 10,
        "profile": {
            "email": "user@example.com"
        }
    }
})
print(nested.user.profile.email)  # user@example.com

5、合并操作

Munch 可以通过update()方法将另一个Munch示例或字典合并进当前实例:

from munch import Munch

a = Munch(foo=1, nested=Munch(x=1, y=2))
b = {"nested": {"y": 20, "z": 30}}

a.update(b)
print(a)
# 结果为Munch({'foo': 1, 'nested': {'y': 20, 'z': 30}})

update() 是 浅合并(shallow merge),如果需要递归合并,可以这样手动实现:

def recursive_update(target, source):
    for key, value in source.items():
        if key in target and isinstance(target[key], dict) and isinstance(value, dict):
            recursive_update(target[key], value)
        else:
            target[key] = value

a = Munch(foo=1, nested=Munch(x=1, y=2))
b = {"nested": {"y": 20, "z": 30}}

recursive_update(a, b)
print(a)
# 输出Munch({'foo': 1, 'nested': Munch({'x': 1, 'y': 20, 'z': 30})})

6、应用场景说明

Munch 的性能总体上略低于原生 dict,这是因为它在属性访问时多了一层 Python 层的函数调用(比如 getattrsetattr 等),但它仍然足够轻量,适合大多数非性能瓶颈场景。

Munch适合用于:

  • 配置读取(如 config.json)
  • CPU 密集或低延迟要求的核心路径
  • 结构化 JSON 响应封装

不建议将Munch用于:

  • 类型严格检查的项目(除非手动封装)
  • 数据探索、Jupyter Notebook 脚本开发
  • 内存/性能受限的环境

进阶功能

1、嵌套写入:创建不存在的子对象

nested = DefaultMunch(dict)
nested.user.info.email = "a@example.com"
print(nested)  # {'user': {'info': {'email': 'a@example.com'}}}

2、序列化(转回 dict)

m = Munch(name="Alice", age=30)
d = m.toDict()
print(type(d))  # dict

3、深度拷贝结构

import copy
m = Munch(a=1, b={"x": 10})
m_copy = copy.deepcopy(m)

4、与 JSON 配合:动态数据结构绑定

import json
from munch import Munch

json_str = '{"user": {"id": 42, "roles": ["admin", "editor"]}}'
m = Munch(json.loads(json_str))
print(m.user.roles)  # ['admin', 'editor']

5、与 Pydantic/配置类组合使用(读取动态配置)

import yaml
from munch import Munch

with open("config.yaml") as f:
    config_dict = yaml.safe_load(f)

config = Munch(config_dict)
print(config.database.host)

实现原理

Munch 的原理很简单:它是在 Python 内置的 dict 类的基础上,通过重写属性访问相关方法,实现了 “点操作”访问字典内容 的能力。

1、继承自 dict

class Munch(dict):
    ...

因此,Munch本质上还是一个字典。

2、 重写了 __getattr____setattr__ 方法

这些方法控制了对象属性的读取和设置:

def __getattr__(self, key):
    try:
        return self[key]
    except KeyError:
        raise AttributeError(f"'Munch' object has no attribute {key}")

def __setattr__(self, key, value):
    self[key] = value

3、__delattr__ 支持删除属性

def __delattr__(self, key):
    try:
        del self[key]
    except KeyError:
        raise AttributeError(f"'Munch' object has no attribute {key}")

这保证了 del m.attr 与 del m[‘attr’] 效果一致。

4、递归嵌套(来自 DefaultMunch.fromDict())

这个功能将嵌套的字典也转换为 Munch:

@classmethod
def fromDict(cls, d):
    return cls({k: cls.fromDict(v) if isinstance(v, dict) else v for k, v in d.items()})

编写一个更高性能的Munch

有了Munch实现原理的理解,接下来我们实现一个轻量级、性能更高的 “点操作字典” 替代方案:DotDict。它模仿了 Munch 的基本行为,但去除了递归转换、类型检查等附加功能,以提高性能。
dot_dict.pyDotDict完整源码

class DotDict(dict):
    """更快的支持点操作的 dict,不做嵌套转换。"""
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError as e:
            raise AttributeError(key) from e

    def __setattr__(self, key, value):
        self[key] = value

    def __delattr__(self, key):
        try:
            del self[key]
        except KeyError as e:
            raise AttributeError(key) from e

test_dot_dict.pyDotDict测试示例

data = DotDict(name="Alice", age=30)

print(data.name)     # 点操作访问
print(data['age'])   # 仍可用原生字典方式

data.city = "Beijing"
print(data['city'])  # 北京
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值