372 lines
9.6 KiB
Markdown
372 lines
9.6 KiB
Markdown
以下是为提供的 Python 代码编写的 **Markdown 格式技术文档**,涵盖了功能说明、类与函数的详细描述、使用示例等。
|
||
|
||
---
|
||
|
||
# `DictObject` 模块技术文档
|
||
|
||
本模块提供了一个增强型字典对象 `DictObject`,支持通过属性访问键值(如 `obj.key`),并可递归封装嵌套数据结构。同时提供了 JSON 序列化支持和多值字典转单值/列表字典的工具函数。
|
||
|
||
## 目录
|
||
|
||
- [核心功能概述](#核心功能概述)
|
||
- [函数 `multiDict2Dict`](#function-multidict2dictmd)
|
||
- [类 `DictObjectEncoder`](#class-dictobjectencoderjsonencoder)
|
||
- [类 `DictObject`](#class-dictobjectdict)
|
||
- [方法概览](#方法概览)
|
||
- [详细方法说明](#详细方法说明)
|
||
- [辅助静态方法 `_wrap` 和 `_dict`](#辅助静态方法-_wrap-和-_dict)
|
||
- [已注释工厂函数 `dictObjectFactory`](#已注释掉的工厂函数-dictobjectfactory建议扩展用)
|
||
- [使用示例](#使用示例)
|
||
- [注意事项与限制](#注意事项与限制)
|
||
|
||
---
|
||
|
||
## 核心功能概述
|
||
|
||
该模块主要实现以下功能:
|
||
|
||
1. **属性式访问字典**:允许像操作对象属性一样读写字典内容。
|
||
2. **自动类型包装**:对嵌套的字典或列表自动转换为 `DictObject` 或其列表形式。
|
||
3. **安全访问机制**:访问不存在的属性返回 `None` 而非抛出异常。
|
||
4. **JSON 可序列化支持**:通过自定义编码器支持复杂对象的 JSON 输出。
|
||
5. **多值字典合并工具**:将多个同名键的值合并为列表。
|
||
|
||
---
|
||
|
||
## Function: `multiDict2Dict(md)`
|
||
|
||
将一个多值字典(如 Web 表单中常见的多个同名参数)转换为标准字典,若某键有多个值,则将其合并为一个列表。
|
||
|
||
### 参数
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `md` | `dict` 或 `MultiDict` | 输入的多值字典 |
|
||
|
||
### 返回值
|
||
`dict`: 合并后的字典。如果某个键出现多次:
|
||
- 第一次:直接赋值;
|
||
- 第二次及以上:转换为列表并追加。
|
||
|
||
### 示例
|
||
```python
|
||
data = {'name': 'Alice', 'hobby': 'reading', 'hobby': 'coding'}
|
||
# 实际输入可能是 MultiDict({'hobby': ['reading', 'coding']})
|
||
result = multiDict2Dict(data)
|
||
# result == {'name': 'Alice', 'hobby': ['reading', 'coding']}
|
||
```
|
||
|
||
> ⚠️ 注意:Python 字典本身不允许多个相同键,此函数适用于模拟场景或多值结构(如 Flask 的 `request.args`)。
|
||
|
||
---
|
||
|
||
## Class: `DictObjectEncoder(JSONEncoder)`
|
||
|
||
用于将 `DictObject` 实例序列化为 JSON 的自定义编码器。
|
||
|
||
### 方法
|
||
#### `.default(o)`
|
||
重写父类方法,当遇到无法序列化的对象时调用。
|
||
|
||
##### 参数
|
||
- `o`: 待序列化的对象。
|
||
|
||
##### 行为
|
||
调用对象的 `_addon()` 方法获取可序列化数据(需用户自行实现 `_addon` 方法)。
|
||
⚠️ 当前实现依赖于对象存在 `_addon()` 方法,否则会报错。
|
||
|
||
##### 示例
|
||
```python
|
||
class MyObj(DictObject):
|
||
def _addon(self):
|
||
return {"custom": "value"}
|
||
|
||
json.dumps(MyObj(), cls=DictObjectEncoder)
|
||
# 输出: {"custom": "value"}
|
||
```
|
||
|
||
> 💡 提示:若未定义 `_addon()`,应避免直接使用此编码器。
|
||
|
||
---
|
||
|
||
## Class: `DictObject(dict)`
|
||
|
||
一个继承自 `dict` 的增强字典类,支持属性式访问、嵌套包装和深拷贝。
|
||
|
||
### 初始化
|
||
```python
|
||
obj = DictObject(key=value, other=[1,2])
|
||
# 或
|
||
obj = DictObject({'key': 'value'})
|
||
```
|
||
|
||
### 特性
|
||
- 支持 `obj.key` 获取/设置 `obj['key']`
|
||
- 自动递归包装嵌套的 `dict` 和 `list`
|
||
- 访问不存在的属性返回 `None`(不抛异常)
|
||
- 支持 `del obj.key` 删除键
|
||
|
||
---
|
||
|
||
### 方法概览
|
||
|
||
| 方法 | 说明 |
|
||
|------|------|
|
||
| `__getattr__(key)` | 属性取值,不存在返回 `None` |
|
||
| `__setattr__(key, value)` | 设置属性 |
|
||
| `__delattr__(key)` | 删除属性,不存在则抛 `AttributeError` |
|
||
| `__setitem__(key, value)` | 设置项,并自动包装值 |
|
||
| `update(*args, **kwargs)` | 更新字典,自动包装新值 |
|
||
| `to_dict()` | 转换为普通嵌套字典 |
|
||
| `copy()` | 深拷贝当前对象 |
|
||
| `_wrap(value)` *(static)* | 包装 dict/list 为 DictObject |
|
||
| `_dict(value)` *(static)* | 解包 DictObject 为原生 dict |
|
||
|
||
---
|
||
|
||
### 详细方法说明
|
||
|
||
#### `__init__(*args, **kwargs)`
|
||
初始化 `DictObject` 实例,并立即更新所有传入的数据。
|
||
|
||
##### 示例
|
||
```python
|
||
d = DictObject(a=1, b={'x': 2})
|
||
print(d.b.x) # 输出: 2
|
||
```
|
||
|
||
---
|
||
|
||
#### `__getattr__(key)`
|
||
使你可以使用点语法访问字典键。
|
||
|
||
- 成功:返回对应值。
|
||
- 失败:返回 `None`(原被注释的 `raise AttributeError` 已禁用)。
|
||
|
||
> 🛑 不推荐用于判断是否存在字段,请使用 `in` 操作符。
|
||
|
||
##### 示例
|
||
```python
|
||
obj = DictObject(name="Bob")
|
||
print(obj.name) # "Bob"
|
||
print(obj.age) # None
|
||
```
|
||
|
||
---
|
||
|
||
#### `__setattr__(key, value)`
|
||
允许通过 `obj.key = value` 设置字典键值。
|
||
|
||
内部调用 `self[key] = value`。
|
||
|
||
##### 示例
|
||
```python
|
||
obj = DictObject()
|
||
obj.city = "Beijing"
|
||
print(obj['city']) # "Beijing"
|
||
```
|
||
|
||
---
|
||
|
||
#### `__delattr__(key)`
|
||
删除指定属性(即字典键)。
|
||
|
||
- 若键不存在,抛出 `AttributeError`。
|
||
- 否则从字典中移除该键。
|
||
|
||
##### 示例
|
||
```python
|
||
del obj.city
|
||
# 等价于 del obj['city']
|
||
```
|
||
|
||
---
|
||
|
||
#### `__setitem__(key, value)`
|
||
设置键值对,并通过 `_wrap()` 自动包装值。
|
||
|
||
这意味着嵌套的字典会被自动转为 `DictObject`。
|
||
|
||
##### 示例
|
||
```python
|
||
obj['nested'] = {'a': 1}
|
||
print(type(obj['nested'])) # <class 'DictObject'>
|
||
```
|
||
|
||
---
|
||
|
||
#### `update(*args, **kwargs)`
|
||
批量更新字典内容,所有新增值都会经过 `_wrap()` 处理。
|
||
|
||
##### 示例
|
||
```python
|
||
obj.update({'list': [1, {'deep': 2}]})
|
||
print(type(obj.list[1])) # DictObject
|
||
```
|
||
|
||
---
|
||
|
||
#### `to_dict()`
|
||
将整个 `DictObject` 结构递归转换为原始的 Python 原生字典(去除 `DictObject` 类型)。
|
||
|
||
##### 返回
|
||
`dict`: 完全由 `dict`, `list`, 基本类型组成的结构,适合 JSON 序列化。
|
||
|
||
##### 示例
|
||
```python
|
||
obj = DictObject(a=DictObject(b=2))
|
||
plain = obj.to_dict()
|
||
print(plain) # {'a': {'b': 2}}
|
||
```
|
||
|
||
---
|
||
|
||
#### `copy()`
|
||
创建一个深拷贝副本,包括所有嵌套结构都重新包装。
|
||
|
||
> ⚠️ 并非完全等同于 `copy.deepcopy()`,但对常见结构足够安全。
|
||
|
||
##### 返回
|
||
新的 `DictObject` 实例。
|
||
|
||
##### 示例
|
||
```python
|
||
new_obj = obj.copy()
|
||
new_obj.a.b = 999
|
||
# 不影响原始 obj
|
||
```
|
||
|
||
---
|
||
|
||
#### `@staticmethod _wrap(value)`
|
||
递归包装输入值中的 `dict` 和 `list`。
|
||
|
||
##### 规则
|
||
- `dict` → `DictObject(dict)`
|
||
- `list` → `[ _wrap(item) for item in list ]`
|
||
- 其他类型保持不变
|
||
|
||
##### 示例
|
||
```python
|
||
wrapped = DictObject._wrap({'x': {'y': 1}})
|
||
print(type(wrapped)) # DictObject
|
||
print(type(wrapped['x'])) # DictObject
|
||
```
|
||
|
||
---
|
||
|
||
#### `@staticmethod _dict(value)`
|
||
与 `_wrap` 相反,将 `DictObject` 或其嵌套结构还原为原生 `dict` / `list`。
|
||
|
||
##### 规则
|
||
- `DictObject` → `.to_dict()`
|
||
- `list` → 递归处理每个元素
|
||
- 其他类型原样返回
|
||
|
||
##### 示例
|
||
```python
|
||
data = DictObject._dict(obj)
|
||
# data 是纯 dict/list 结构
|
||
```
|
||
|
||
---
|
||
|
||
## 已注释掉的工厂函数 `dictObjectFactory`(建议扩展用)
|
||
|
||
该函数原本设计用于根据类名动态创建特定子类实例。
|
||
|
||
```python
|
||
# 示例原型(当前被注释):
|
||
# obj = dictObjectFactory("User", name="Tom", age=30)
|
||
```
|
||
|
||
### 功能逻辑(潜在用途)
|
||
1. 查找 `DictObject` 的子类中哪个满足 `isMe(_klassName__) == True`
|
||
2. 若找到,则实例化该子类;否则回退到 `DictObject`
|
||
|
||
> ✅ 可作为插件式对象注册机制的基础,适合 ORM 或配置系统。
|
||
|
||
### 当前状态
|
||
- 被注释,暂未启用。
|
||
- 使用时需确保各子类实现了 `isMe(class_name)` 类方法。
|
||
|
||
---
|
||
|
||
## 使用示例
|
||
|
||
### 示例 1:基础用法
|
||
```python
|
||
user = DictObject(
|
||
name="Alice",
|
||
profile={"age": 28, "city": "Shanghai"},
|
||
hobbies=["reading", "hiking"]
|
||
)
|
||
|
||
print(user.name) # Alice
|
||
print(user.profile.city) # Shanghai
|
||
user.email = "alice@example.com"
|
||
print(user.email) # alice@example.com
|
||
```
|
||
|
||
### 示例 2:JSON 序列化
|
||
```python
|
||
class Person(DictObject):
|
||
def _addon(self):
|
||
return self.to_dict() # 或添加额外元信息
|
||
|
||
p = Person(name="Bob", info={"job": "Engineer"})
|
||
json_str = json.dumps(p, cls=DictObjectEncoder, indent=2)
|
||
print(json_str)
|
||
# {
|
||
# "name": "Bob",
|
||
# "info": {
|
||
# "job": "Engineer"
|
||
# }
|
||
# }
|
||
```
|
||
|
||
### 示例 3:多值字典处理
|
||
```python
|
||
form_data = {'tag': 'python', 'tag': 'web', 'author': 'admin'}
|
||
# 实际可能是 MultiDict({'tag': ['python', 'web'], 'author': ['admin']})
|
||
|
||
flat = multiDict2Dict(form_data)
|
||
print(flat['tag']) # ['python', 'web'] (如果是真实多值)
|
||
```
|
||
|
||
---
|
||
|
||
## 注意事项与限制
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| ❌ `__getattr__` 返回 `None` | 导致无法区分“无此属性”和“属性值为 None” |
|
||
| ⚠️ `_addon()` 必须实现 | 否则 `DictObjectEncoder` 会失败 |
|
||
| 🔁 `_wrap` 是递归的 | 大型嵌套结构可能影响性能 |
|
||
| 🧩 子类工厂未启用 | 如需动态构造,请解注并完善 `isMe()` 逻辑 |
|
||
| 📦 不支持 tuple/set 自动包装 | 仅处理 `dict` 和 `list` |
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
`DictObject` 提供了一种简洁优雅的方式来处理嵌套数据结构,特别适用于:
|
||
|
||
- 配置文件解析(YAML/JSON)
|
||
- API 响应数据建模
|
||
- Web 请求参数处理
|
||
- 构建轻量级数据传输对象(DTO)
|
||
|
||
结合 `multiDict2Dict` 和 `DictObjectEncoder`,可在前后端交互、日志记录、序列化等场景中大幅提升开发效率。
|
||
|
||
---
|
||
|
||
📌 **建议改进方向**:
|
||
- 添加类型提示(Type Hints)
|
||
- 引入 `__dir__()` 支持 IDE 自动补全
|
||
- 实现更健壮的工厂模式或注册中心
|
||
- 增加单元测试覆盖率
|
||
|
||
---
|
||
|
||
📄 *文档版本:v1.0*
|
||
📅 *最后更新:2025-04-05* |