5

From https://stackoverflow.com/a/44880260/156458

Note that the __dict__ attribute of custom Python class instances is a descriptor; the instance itself doesn't have the attribute, it is the class that provides it (so type(instance).__dict__['__dict__'].__get__(instance) is returned). object.__dict__ may exist, but object.__dict__['__dict__'] does not.

Why is __dict__ attribute of a custom Python class instance a descriptor of the class, instead of an actual attribute of the instance?

S.B
  • 13,077
  • 10
  • 22
  • 49
Tim
  • 1
  • 141
  • 372
  • 590
  • 3
    All instances of a class have the same descriptor, so there's no need to duplicate it in every instance. – Barmar Oct 05 '17 at 00:08
  • 1
    @Barmar but different instances of a class have different instance attributes, so need their own `__dict__`'s to store their different instance attributes. correct? – Tim Oct 05 '17 at 00:13

2 Answers2

10

It's tempting to say that __dict__ has to be a descriptor because implementing it as a __dict__ entry would require you to find the __dict__ before you can find the __dict__, but Python already bypasses normal attribute lookup to find __dict__ when looking up other attributes, so that's not quite as compelling as it initially sounds. If the descriptors were replaced with a '__dict__' key in every __dict__, __dict__ would still be findable.

There's some space savings by not having a key for '__dict__' in every __dict__, but that's not the big reason. There's also time saved by not having to set a '__dict__' key, and time and space saved by not creating a circular reference, and these benefits are all really nice, but they're still probably smaller than the next thing.

The big thing requiring __dict__ to be a descriptor is handling attempts to reassign or delete an object's __dict__. If attribute lookup for __dict__ went through a __dict__ key, then reassigning someobj.__dict__ would reassign the dict key without changing what dict Python actually looks in to find someobj's attributes. __dict__ needs to be a descriptor so it stays in sync with the actual C-level struct slot Python looks in to find an object's dict.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 1
    Thanks. What do you mean by "Python already bypasses normal attribute lookup to find `__dict__`"? – Tim Oct 05 '17 at 16:29
  • @Tim: When looking up `someobj.foo`, Python [finds the instance dict](https://github.com/python/cpython/blob/v3.6.3/Objects/object.c#L1067) using the type's C-level `tp_dictoffset` rather than by looking up `someobj.__dict__` through normal attribute lookup. – user2357112 Oct 05 '17 at 17:08
  • 1
    Thanks. (1) Does the lookup process for `__dict__` also apply to other special attributes whose names both begin and end with two underscores, such as `__name__, __class__, __new__, __init__, __str__, __repr__`? (2) [will the lookup for self-defined attributes whose names both begin and end with two underscores (e.g. `__madeupAttribute__`) be the same as the lookup for those special attributes?](https://stackoverflow.com/q/46594480) – Tim Oct 05 '17 at 21:03
2

Because it's the attribute that allows instances to have custom attributes. It's where new custom attributes of an instance are typically be stored. Some magic is required to avoid circular references (you wouldn't want to look up __dict__ on an instance to find __dict__ on that instance). Class descriptors are one of two Python mechanisms to have this sort of magic for instance attribute lookup.

wim
  • 338,267
  • 99
  • 616
  • 750
Sam Hartman
  • 6,210
  • 3
  • 23
  • 40