I subclass dict
so that the attributes are identical to the keys:
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__'})
and it works as expected:
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
But if I re-write __setattr__
as
... def __setattr__(self, key, value): self[key] = value ...
then __dict__
will be re-created in initialization and the attributes will turn inaccessible:
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'
Adding a paired __getattr__
as below will get around the AttributeError
... def __getattr__(self, key): return self[key] ...
but still __dict__
is cleared:
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
Thanks for any explanations.
Advertisement
Answer
There’s no reinitialization. Your problem is that self.__dict__ = self
hits your __setattr__
override. It’s not actually changing the dict used for attribute lookups. It’s setting an entry for the '__dict__'
key on self
and leaving the attribute dict untouched.
If you wanted to keep your (pointless) __setattr__
override, you could bypass it in __init__
:
object.__setattr__(self, '__dict__', self)
but it’d be easier to just take out your __setattr__
override. While you’re at it, take out that __repr__
, too – once you fix your code, the only reason there would be a '__dict__'
key is if a user sets it themselves, and if they do that, you should show it.