2

What is the __weakrefoffset__ attribute for? What does the integer value signify?

>>> int.__weakrefoffset__ 
0
>>> class A: 
...     pass 
... 
>>> A.__weakrefoffset__ 
24
>>> type.__weakrefoffset__ 
368
>>> class B: 
...     __slots__ = () 
...
>>> B.__weakrefoffset__
0

All types seem to have this attribute. But there's no mention about that in docs nor in PEP 205.

wim
  • 338,267
  • 99
  • 616
  • 750
  • See answer of user `user2357112` in https://stackoverflow.com/questions/52011430/python-which-types-support-weak-references (in short: nonzero `__weakrefoffset__` means given type support weak references) – Daweo May 17 '19 at 18:46
  • I saw that post already but it leaves a lot to be desired - doesn't explain what the value means, and doesn't provide any references for the claims. – wim May 17 '19 at 18:47
  • 1
    `__weakrefoffset__` is completely undocumented, so it's hard to provide doc links. Literally the only place the string `__weakrefoffset__` shows up in the entire CPython repository is in the [PyMemberDef that defines it](https://github.com/python/cpython/blob/v3.7.3/Objects/typeobject.c#L367). – user2357112 May 17 '19 at 19:27
  • Anyway, I've added some more explanation to my answer over on that question, plus a source link for `__weakrefoffset__` and a doc link for `tp_weaklistoffset`, because it's useful information to have there. – user2357112 May 17 '19 at 20:02

2 Answers2

4

In CPython, the layout of a simple type like float is three fields: its type pointer, its reference count, and its value (here, a double). For list, the value is three variables (the pointer to the separately-allocated array, its capacity, and the used size). These types do not support attributes or weak references to save space.

If a Python class inherits from one of these, it’s critical that it be able to support attributes, and yet there is no fixed offset at which to place the __dict__ pointer (without wasting memory by putting it at a large unused offset). So the dictionary is stored wherever there is room, and its offset in bytes is recorded in the type. For base classes where the size is variable (like tuple, which includes all its pointers directly), there is special support for storing the __dict__ pointer past the end of the variable-size section (e.g., type("",(tuple,),{}).__dictoffset__ is -8).

The situation with weak references is exactly analogous, except that there is no support for (subclasses of) variable-sized types. A __weakrefoffset__ of 0 (which is conveniently the default for C static variables) indicates no support, since of course the object’s type is known to always be at the beginning of its layout.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
0

It is referenced in PEP 205 here:

Many built-in types will participate in the weak-reference management, and any extension type can elect to do so. The type structure will contain an additional field which provides an offset into the instance structure which contains a list of weak reference structures. If the value of the field is <= 0, the object does not participate. In this case, weakref.ref(), <weakdict>.__setitem__() and .setdefault(), and item assignment will raise TypeError. If the value of the field is > 0, a new weak reference can be generated and added to the list.

And you can see exactly how that works in the source code here.

Check out the other answer for more why it exists.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
MyNameIsCaleb
  • 4,409
  • 1
  • 13
  • 31