为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

温温酱 2021-09-24 15:06:45
大家圣诞快乐,我正在实现一个允许属性访问的自定义字典,例如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 itemclass 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):我的测试不会变成绿色。
查看完整描述

3 回答

?
慕娘9325324

TA贡献1783条经验 获得超4个赞

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

您真正想要的是将collections.abc.MutableMapping子类化- 它的构造方式可确保任何项目检索都将通过__getitem__,(不过,您必须实现文档中指出的方法,包括__delitem__,__setitem____iter__- 推荐是将实际数据保留为方法中.data创建的属性中的普通字典__init__)。

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


查看完整回答
反对 回复 2021-09-24
?
肥皂起泡泡

TA贡献1829条经验 获得超6个赞

在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


查看完整回答
反对 回复 2021-09-24
?
守着星空守着你

TA贡献1799条经验 获得超8个赞

哇,尝试使用未注释的方式运行以下代码 __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']))

非常非常奇怪


查看完整回答
反对 回复 2021-09-24
  • 3 回答
  • 0 关注
  • 237 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号