大家圣诞快乐,
我正在实现一个允许属性访问的自定义词典,例如dct.attribute
。字典可以嵌套,因此dct.nested_dct.attribute
也应该是可能的。除star-star-unpacking之外,这已经很好地工作了。我认为我能够表达出我想用代码做得比单词做得更好的事情。这是我正在写的课。测试应该非常清楚地说明其作用:
class DotDict(dict):
def __getattr__(self, item):
return self.__getitem__(item)
def __getitem__(self, item):
item = super().__getitem__(item)
if isinstance(item, dict):
return self.__class__(item)
return item
class TestDotDict:
@pytest.fixture
def dot_dict(self):
input_dict = dict(
a=1,
b=dict(
c=2,
d=3,
)
)
return DotDict(input_dict)
def test_can_access_by_dot(self, dot_dict):
assert dot_dict.a == 1
def test_returned_dicts_are_dot_dicts(self, dot_dict):
b_dict = dot_dict["b"]
assert isinstance(b_dict, DotDict)
assert b_dict.c == 2
def test_getting_item_also_returns_dot_dicts(self, dot_dict):
b_dict = dot_dict["b"]
assert isinstance(b_dict, DotDict)
assert b_dict.c == 2
def test_unpack_as_function_arguments_yields_dot_dicts_for_children(self, dot_dict):
# this is failing
def checker(a, b):
assert a == 1
assert b.c == 2
checker(**dot_dict)
如评论中所述,最后一次测试失败。有人知道如何解决吗?
根据以下问题的答案:star unpacking for own classes,我认为我需要继承自collections.abc.Mapping
和dict
。但是,这并不能解决问题。
我当时认为这可能与我不太清楚的MRO有关。但是无论我是否将类定义更改为
class DotDict(Mapping, item):
或
class DotDict(item, Mapping):
我的测试不会变成绿色。
答案 0 :(得分:1)
在test_star_star_mapping_maintains_child_dot_dicts
中,您创建的是dict
而不是DotDict
,因此重构为:
def test_star_star_mapping_maintains_child_dot_dicts(self, dot_dict):
obtained_via_star = DotDict(dict(**dot_dict))
b_dict = obtained_via_star["b"]
assert b_dict.c == 2
将通过测试,因为您现在正在创建DotDict
。也许您想删除部分dict(**dot_dict)
,以便此版本也可以使用:
def test_star_star_mapping_maintains_child_dot_dicts(self, dot_dict):
obtained_via_star = DotDict(**dot_dict)
b_dict = obtained_via_star["b"]
assert b_dict.c == 2
答案 1 :(得分:1)
您面临的问题是yu试图建立在本机dict
上-对于此类,__getitem__
只是可以检索其值的几种方法之一。由于在python中实现了命令的实现方式,出于历史和性能方面的考虑,有很多方法可以完全绕过__getitem__
,因此嵌套字典永远不会被“包装”在DotDict中。 (例如:.values()
,items()
和星图甚至可能会绕过这些)
您真正想拥有的是collections.abc.MutableMapping的子类-它的构造方式确保可以通过__getitem__
进行任何项目检索(您必须实现文档中指定的方法但是,包括__delitem__
,__setitem__
和__iter__
的建议是将实际数据作为简单的判词保留在.data
方法中创建的__init__
属性中)。
可以看出,这也使您可以更好地控制数据,例如,使您可以直接在setitem上将数据包装在自定义类中,而jsut则不在乎属性检索-或相反,将所有映射存储为普通字典以节省内存并提高效率,并将其包装在检索中。
答案 2 :(得分:0)
哇,尝试用未注释的__iter__
class DotDict(dict):
# def __iter__(self):
# return super().__iter__()
def __getattr__(self, item):
return self.__getitem__(item)
def __getitem__(self, item):
item = super().__getitem__(item)
if isinstance(item, dict):
return self.__class__(item)
return item
d = DotDict({'a': {'b':'c'}})
print(type(dict(**d)['a']))
非常非常奇怪