说我有两个(简单的玩具)嵌套数据结构,如下所示:
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生成属性,这也是需要的。