As far as I understood, gc.get_referrers(my_obj)
should return a list of the objects that refer to my_obj
.
However, I'm currently seeing this behaviour:
import sys
import gc
my_obj = []
ref_1 = my_obj
ref_2 = my_obj
sys.getrefcount(my_obj) # Returns 4, as expected
gc.get_referrers(my_obj)
This last command returns the following:
[
{
'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'sys': <module 'sys' (built-in)>,
'gc': <module 'gc' (built-in)>,
'my_obj': [],
'ref_1': [],
'ref_2': []
}
]
I was expecting to receive a list of 4 objects, but gc.get_referrers(my_obj)
is returning a list that contains only one dictionary instead.
What does this dictionary represent? Where is it documented? And why is gc.get_referrers(my_obj)
returning it instead of the 4 objects I was expecting?
Objects can be referenced by other Python objects, but there are also lots of internal references from the interpreter itself.
Internal references include local variables, temporary results in expressions, and and function-call argument lists.
sys.getrefcount()
simply retrieves the reference count field of the object, so it will count all these internal references. It will likely be 1 more than the number of permanent references, because the object itself is in the argument list of the call to sys.getrefcount()
, so its reference count is incremented during the call.
Global variables are not first-class objects in Python (contrast with Lisp, where global variables are implemented using symbol objects, which have a value cell containing the value). They're implemented as items in the globals()
dictionary. So in your code snippet, when you created several global variables referencing the list, gc.get_referrers()
simply returned that dictionary.
gc.get_referrers()
can only return Python objects, so its result will not include internal references. I suppose they could have devised some way to represent these as some kind of proxy object (it might print as something like <arglist #12345>
), but since these are often transient it wasn't felt necessary (consider that the argument to gc.get_referrers()
would no longer exist when you're examining the return value).
Local variables are implemented using internal references (the reference comes from the stack frame containing local state), so they normally don't show up in gc.get_referrers()
results.
However, if you call locals()
, this forces a dictionary to be created containing all the local variables. Since this is a normal object, it will be returned after that point, similar to the way you got the globals()
dictionary in your top-level code. sys.getrefcount()
will count both the internal reference and the reference from this dictionary.