猿问

自定义字典以在 ** 上维护它的 __getitem__ (star-star-unpacking)

大家圣诞快乐,


我正在实现一个允许属性访问的自定义字典,例如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.Mappingand dict。然而,这并没有解决问题。


我在想这可能与我不太清楚的 MRO 有关。但是无论我是否将类定义更改为


class DotDict(Mapping, item):

或者


class DotDict(item, Mapping):

我的测试不会变成绿色。


温温酱
浏览 238回答 3
3回答

慕娘9325324

您面临的问题是,您正在尝试构建本机dict - 对于此类,__getitem__这只是可以检索其值的几种方法之一。由于字典在 Python 中的实现方式,出于历史和性能原因,有很多方法可以完全绕过__getitem__,因此,嵌套字典永远不会“包装”在 DotDict 中。(例如:.values(), items(), 和 starmap 甚至可能会绕过这些)您真正想要的是将collections.abc.MutableMapping子类化- 它的构造方式可确保任何项目检索都将通过__getitem__,(不过,您必须实现文档中指出的方法,包括__delitem__,__setitem__和__iter__- 推荐是将实际数据保留为方法中.data创建的属性中的普通字典__init__)。意识到这也使您可以更好地控制数据,例如,使您能够将数据直接包装在 setitem 上的自定义类中,并且不关心属性检索 - 或者,相反,存储任何映射作为普通字典以节省内存和提高效率,并将其包装在检索中。

肥皂起泡泡

在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

守着星空守着你

哇,尝试使用未注释的方式运行以下代码 __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 itemd = DotDict({'a': {'b':'c'}})print(type(dict(**d)['a']))非常非常奇怪
随时随地看视频慕课网APP

相关分类

Python
我要回答