自定义词典以维护其在**(starunpacking)上的\uuu getitem\uuuuuuuuuu

2024-04-28 22:35:37 发布

您现在位置:Python中文网/ 问答频道 /正文

大家圣诞快乐

我正在实现一个允许属性访问的自定义字典,例如dct.attribute。字典可以嵌套,因此dct.nested_dct.attribute也应该是可能的。这已经很好地工作了,除了明星解包。我想我能用代码比用文字更好地表达我想做的事情。这是我正在写的课。测试应该非常清楚地解释它的作用:

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.Mappingdict继承。然而,这并没有解决问题

我在想这可能和我不太清楚的MRO有关。但不管我把类定义改成

class DotDict(Mapping, item):

或者

class DotDict(item, Mapping):

我的测试不会变绿


Tags: testselfreturndefassertitemdotmapping
3条回答

您所面临的问题是,yu正在尝试构建一个本机的dict——对于这个类,__getitem__只是检索其值的几种方法之一。由于在Python中实现dict的方式,出于历史和性能的原因,有很多方法可以完全绕过__getitem__,因此,嵌套字典永远不会“包装”在DotDict中(例如:.values()items()和星图可能会绕过这些)

您真正想要的是子类collections.abc.MutableMapping——它的构造方式确保任何项检索都要经过__getitem__,(但是您必须实现文档中指出的方法,包括__delitem____setitem____iter__-建议将实际数据作为普通词汇保存在__init__方法中创建的.data属性中

请注意,这也使您能够更好地控制数据,例如,使您能够将数据直接包装在setitem上的自定义类中,而jsut不关心属性检索,或者相反,将任何映射存储为普通字典,以节省内存和提高效率,并在检索时将其包装

哇,试着用未注释的__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']))

非常非常奇怪

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

相关问题 更多 >