什么时候重新初始化__dict__?

时间:2019-05-21 07:57:53

标签: python dictionary

我将dict子类化,以使属性与键相同:

class DictWithAttr(dict):
    def __init__(self, *args, **kwargs):
        self.__dict__ = self
        super(DictWithAttr, self).__init__(*args, **kwargs)
        print(id(self), id(self.__dict__))

    def copy(self):
        return DictWithAttr(self.__dict__)

    def __repr__(self):
        return repr({k:v for k, v in self.items() if k != '__dict__'})

它可以按预期工作:

d = DictWithAttr(x=1, y=2)    # 139917201238328 139917201238328
d.y = 3
d.z = 4
d['w'] = 5
print(d)                      # {'x': 1, 'y': 3, 'z': 4, 'w': 5}
print(d.__dict__)             # {'x': 1, 'y': 3, 'z': 4, 'w': 5}
print(d.z, d.w)               # 4 5

但是如果我将__setattr__重写为

    ...
    def __setattr__(self, key, value):
        self[key] = value
    ...

然后__dict__将在初始化时重新创建,并且属性将变得不可访问:

d = DictWithAttr(x=1, y=2)    # 140107290540344 140107290536264
d.y = 3
d.z = 4
d['w'] = 5
print(d)                      # {'x': 1, 'y': 3, 'z': 4, 'w': 5}
print(d.__dict__)             # {}
print(d.z, d.w)               # AttributeError: 'DictWithAttr' object has no attribute 'z'

按如下所示添加成对的__getattr__会绕过AttributeError

    ...
    def __getattr__(self, key):
        return self[key]
    ...

但仍然清除__dict__

d = DictWithAttr(x=1, y=2)    # 139776897374520 139776897370944
d.y = 3
d.z = 4
d['w'] = 5
print(d)                      # {'x': 1, 'y': 3, 'z': 4, 'w': 5}
print(d.__dict__)             # {}
print(d.z, d.w)               # 4 5

谢谢您的解释。

2 个答案:

答案 0 :(得分:2)

没有重新初始化。您的问题是self.__dict__ = self达到您的__setattr__替代值。它实际上并没有改变用于属性查找的字典。它将为'__dict__'上的self键设置一个条目,而属性dict则保持不变。

如果您想保留(毫无意义的)__setattr__覆盖,则可以在__init__中绕过它:

object.__setattr__(self, '__dict__', self)

,但是只需删除您的__setattr__替代项会更容易。当您使用它时,也要取出__repr__-修复代码后,唯一有'__dict__'键的原因是用户自己进行设置,如果用户自己设置的话,您应该显示它。

答案 1 :(得分:0)

要实现所需的功能,应覆盖__getattr____setattr____delattr__

class DictWithAttr(dict):

    def __getattr__(self, name):
        return self[name]

    __setattr__ = dict.__setitem__

    def __delattr__(self, name):
        del self[name]

    def __dir__(self):
        return dir({}) + list(self.keys())

user2357112指出了问题的原因。