pythonmemoryctypesreference-counting

Why ctypes.c_long.from_address.value yields reference count of an object?


In this pycon conference the presenter says that ctypes.c_long.from_address(id(SOME_OBJECT)).value would yields the reference count of an object. I tried to find this in the documentation but found nothing that helps me. The doc says:

from_address(address)

This method returns a ctypes type instance using the memory specified by address which must be an integer.

This method, and others that indirectly call this method, raises an auditing event ctypes.cdata with argument address.

It didn't mentioned to reference count (at least I can't figure it out). I tried it and this seems correct:

Python 3.13.1 (main, Dec  3 2024, 17:59:52) [Clang 16.0.0 (clang-1600.0.26.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes, sys
>>> name = "some random name 123"
>>> sys.getrefcount(name)
2
>>> ctypes.c_long.from_address(id(name)).value
1
>>> x, y = name, name
>>> sys.getrefcount(name)
4
>>> ctypes.c_long.from_address(id(name)).value
3

Can someone explain this?


Solution

  • In CPython, id(obj) returns the C struct PyObject* of obj. On a typical GIL-enabled Python 3.13 build, the first member of that structure is the reference count. It is an implementation detail.

    c_long is not the correct type, and there is no guarantee what the correct type will be - it's likely to be different in Python 3.14. There is also no guarantee that this field will be the first member on all Python builds. The struct definition looks very different on a GIL-disabled build, for example.