Python递归处理嵌套数据时的设计模式(动态属性访问)

为了更好的处理嵌套数据,如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 类中的 getitemsetitem 魔法方法来实现字典形式的属性访问和设置。

_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 数据,使得访问和修改数据更加直观。
用户界面开发:在处理嵌套的界面组件树时,通过这种设计可以更方便地操作组件属性。

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值