我将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
谢谢您的解释。
答案 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指出了问题的原因。