Munch,用對象的訪問方式訪問dict
- 基本使用
- 1、創建一個 Munch 對象
- 2、使用字典初始化
- 3、訪問不存在的字段
- 4、嵌套結構支持
- 5、合并操作
- 6、應用場景說明
- 進階功能
- 1、嵌套寫入:創建不存在的子對象
- 2、序列化(轉回 dict)
- 3、深度拷貝結構
- 4、與 JSON 配合:動態數據結構綁定
- 5、與 Pydantic/配置類組合使用(讀取動態配置)
- 實現原理
- 1、繼承自 dict
- 2、 重寫了 `__getattr__` 和 `__setattr__` 方法
- 3、`__delattr__` 支持刪除屬性
- 4、遞歸嵌套(來自 DefaultMunch.fromDict())
- 編寫一個更高性能的Munch
Munch 是 Python 中一個小巧實用的庫,它將字典(dict)擴展為支持“點操作”的對象訪問方式,即:
data = Munch({"name":"Alice", "age":18})
print(data.name) # 而不是 data["name"]
它特別適用于配置讀取、JSON 響應處理、動態數據訪問等場景。
基本使用
1、創建一個 Munch 對象
from munch import Munchm = Munch()
m.name = "Alice"
m.age = 30print(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 DefaultMunchm = 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 Muncha = 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] = valuea = 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 層的函數調用(比如 getattr、setattr 等),但它仍然足夠輕量,適合大多數非性能瓶頸場景。
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 Munchjson_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 Munchwith 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.py
,DotDict完整源碼:
class DotDict(dict):"""更快的支持點操作的 dict,不做嵌套轉換。"""def __getattr__(self, key):try:return self[key]except KeyError as e:raise AttributeError(key) from edef __setattr__(self, key, value):self[key] = valuedef __delattr__(self, key):try:del self[key]except KeyError as e:raise AttributeError(key) from e
test_dot_dict.py
,DotDict測試示例:
data = DotDict(name="Alice", age=30)print(data.name) # 點操作訪問
print(data['age']) # 仍可用原生字典方式data.city = "Beijing"
print(data['city']) # 北京