为了更好的处理嵌套数据,如JSON,YAML文件。采用
装饰器@classmethod 加 递归处理 的设计模式比较好。
from addict import Dict
class ConfigDict(Dict):
def __init__(__self, *args, **kwargs):
object.__setattr__(__self, '__parent', kwargs.pop('__parent', None))
object.__setattr__(__self, '__key', kwargs.pop('__key', None))
object.__setattr__(__self, '__frozen', False)
for arg in args:
if not arg:
continue
# Since ConfigDict.items will convert LazyObject to real object
# automatically, we need to call super().items() to make sure
# the LazyObject will not be converted.
if isinstance(arg, ConfigDict):
for key, val in dict.items(arg):
__self[key] = __self._hook(val)
elif isinstance(arg, dict):
for key, val in arg.items():
__self[key] = __self._hook(val)
elif isinstance(arg, tuple) and (not isinstance(arg[0], tuple)):
__self[arg[0]] = __self._hook(arg[1])
else:
for key, val in iter(arg):
__self[key] = __self._hook(val)
for key, val in dict.items(kwargs):
__self[key] = __self._hook(val)
@classmethod
def _hook(cls, item):
# avoid to convert user defined dict to ConfigDict.
if isinstance(item, dict):
return cls(item)
elif isinstance(item, (list, tuple)):
return type(item)(cls._hook(elem) for elem in item)
return item
data = {
'name': 'example',
'details': {
'age': 30,
'hobbies': ['reading', 'hiking', {'name': 'coding', 'level': 'advanced'}]
},
'active': True
}
config = ConfigDict(data)
# 访问数据
print(config.name) # 输出: example
print(config.details.age) # 输出: 30
print(config.details.hobbies[0]) # 输出: reading
print(config.details.hobbies[2].name) # 输出: coding
# 修改数据
config.details.age = 31
print(config.details.age) # 输出: 31
# 添加新数据
config.details.hobbies.append('swimming')
print(config.details.hobbies) # 输出: ['reading', 'hiking', {'name': 'coding', 'level': 'advanced'}, 'swimming']
# 动态添加新键值对
config.new_key = 'new_value'
print(config.new_key) # 输出: new_value
在 ConfigDict 类中,__self[key] 实际上是调用了 addict.Dict 类中的 getitem 和 setitem 魔法方法来实现字典形式的属性访问和设置。
_hook 方法就是一个递归函数,它处理嵌套的字典和列表,确保所有子结构都被正确转换为 ConfigDict 实例
这种设计方式:
1、灵活性和可扩展性:
通过装饰器模式,ConfigDict 扩展了字典的功能,使得代码更加灵活,可以通过简单的点符号来访问和修改嵌套数据。如访问属性和修改属性非常方便,
print(config.details.hobbies) # 输出: ['reading', 'hiking', {'name': 'coding', 'level': 'advanced'}]
print(config.details.hobbies[2].name) # 输出: coding
config.details.hobbies.append('swimming')
print(config.details.hobbies) # 输出: ['reading', 'hiking', {'name': 'coding', 'level': 'advanced'}, 'swimming']
这样使得嵌套字典结构中的每个元素都可以通过点符号访问,并且可以灵活的读取和修改嵌套数据。
2、简洁性和可读性:
递归处理模式使得对嵌套结构的处理更加简洁。所有嵌套结构都通过统一的方式处理,代码更容易理解和维护。
3、增强的功能:
ConfigDict 保留了字典的所有原始功能,同时增加了新功能,如点符号访问和自动递归处理嵌套结构。
应用场景
这种设计方式广泛应用于需要处理复杂嵌套数据结构的场景,例如:
配置管理:应用程序的配置文件通常是嵌套的 YAML 或 JSON 文件。通过这种设计,可以更方便地加载和管理配置。
数据处理:处理从 API 返回的嵌套 JSON 数据,使得访问和修改数据更加直观。
用户界面开发:在处理嵌套的界面组件树时,通过这种设计可以更方便地操作组件属性。