嵌套字典到具有属性的类对象字典

时间:2020-03-05 15:30:54

标签: python dictionary dynamic properties nested

说我有两个(简单的玩具)嵌套数据结构,如下所示:

d = dict(zip(list('abc'), list(range(3))))
nested_dict = {k:d.copy() for k in d}
nested_listof_dict = {k:[d.copy() for _ in range(3)] for k in d} 

现在,我想使其表现得更像“常规”类对象(意味着可按点索引)

class dictobj(dict):
    def __init__(self, data: dict, name):
        data['_name'] = name
        super().__init__(data)
        for name, item in data.items():
            if isinstance(item, (list, tuple)):
                setattr(self, name, [dictobj(x, name) if isinstance(x, dict) else x for x in item])
            else:
                setattr(self, name, dictobj(item, name) if isinstance(item, dict) else item)

    def __repr__(self):
        return f"{self['_name']}"


data_dictobj = dictobj(data, 'test')  # size 1185 bytes

对于嵌套字典和nested_listof_dict都很好用

assert nested_listof_dict.a[0].b == nested_listof_dict['a'][0]['b']

但是,由于属性和字典都是可变的,因此可能会发生

nested_listof_dict['a'][0]['b'] = 2
assert nested_listof_dict.a[0].b != nested_listof_dict['a'][0]['b']  # unwanted behavior

因此,将属性实现为属性将是一个好主意。我认为,由于闭包作用域,避免使用lambda函数可能是一个好主意。首先看实现getter的方法,我专注于nested_dict,因为它是一个更简单的结构。

class dictobj(dict):

    def __init__(self, data: dict, name):

        def make_property(self, name, item):
            def getter(self):
                return dictobj(item, name) if isinstance(item, dict) else item
            setattr(self.__class__, name, property(getter))
            # def setter(self, value):
            #     if not isinstance(value, type(item)):
            #         raise ValueError(f'cannot change the data structure, expected '+
            #                      f'{type(item).__name__} got {type(value).__name__}')
            #     self[name] = value
            # setattr(self.__class__, name, property(getter, setter))

        data['_name'] = name
        super().__init__(data)
        for name, item in data.items():
            if isinstance(item, (list, tuple)):
                setattr(self, name, [dictobj(x, name) if isinstance(x, dict) else x for x in item])
            else:
                make_property(self, name, item)

    def __repr__(self):
        return f"{self['_name']}"

然后测试是否可以不再设置属性

d = dictobj(d, 'test')
# d.a = 1  # fails as should: "AttributeError: can't set attribute"
# d.a.a = 1  # fails as should: "AttributeError: can't set attribute"

但是我仍然不知所措,观察到以下行为:

print(d.a)  # returns object "a" - as desired
print(d.a)  # returns 0 - second call returns the nested value

我不知道如何避免这种行为的发生。 除此之外,我还想生成一个setter来强制维护数据结构。毫不奇怪,我在上面写的二传手毫无注释,也会产生意想不到的行为

d.a = {1}  # ValueError: cannot change the data structure, expected dict got set - as desired
d.a.a = 2  # AttributeError: 'int' object has no attribute 'a'
d.a = 2 
assert d.a == 0 and d['a'] == 2  # again unintended

我想了解我在做错什么,并做好这项工作。还应该指出,我什至没有考虑过为nested_listof_dict生成属性,这也是需要的。

1 个答案:

答案 0 :(得分:1)

munch确实满足了我的需求