I wanted to be able to get a tuple of references to any existing object instances of a class. What I came up with was:
import gc
def instances(theClass):
instances = []
gc.collect()
for i in gc.get_referrers(theClass):
if isinstance(i, theClass):
instances.append(i)
return tuple(instances)
If the above code is entered at the Python intepreter prompt, then you can do the below:
>>> class MyClass(object):
>>> pass
>>> c = MyClass()
>>> instances(MyClass)
(<__main__.MyClass object at 0x100c616d0>,)
Hooray. But then it seems as though gc.collect()
doesn't actually do anything inside the function:
>>> del c
>>> instances(MyClass)
(<__main__.MyClass object at 0x100c616d0>,)
But gc.collect()
works when outside the function:
>>> del c
>>> gc.collect()
>>> instances(MyClass)
()
So, my question is: How do I make gc.collect()
actually do a full collection when inside the function (and why doesn't it work as is)? With corollary question: Is there a better way to accomplish the same goal of returning a tuple with references to object instances for a specific class?
NB: This was all tried in Python 2.7.3. I haven't yet tried it in Python 3, but my goal would be to have a function that works in either (or at least which can be converted with 2to3).
Edited (per answer below) to clarify that the issue was really about interactive mode, not the gc.collect()
function per se.
I think there's a simpler, more reliable way to get the information you want, without rummaging around in
gc
: you can make the class responsible for keeping track of its instances.Here I'm using a metaclass to attach a list of instances to each subclass of InstanceTracker, and overriding
__new__
to add each created instance to the list. (This is Python 3 code, it'll need adapting a bit to work with Python 2.)Notes: This code might need tweaking, depending on whether you want to keep track of instances of subclasses and so on. If you want instances to be removed when they are garbage-collected, you need to override
__del__
inInstanceTracker
. You may also be able to simplify it to get rid of the metaclass, if you only need to track instances of one of the classes in your system.When you work in interactive mode, there's a magic built-in variable
_
that holds the result of the last expression statement you ran:When you delete the
c
variable,del c
isn't an expression, so_
is unchanged:_
is keeping a reference to theMyClass
instance. When you callgc.collect()
, that's an expression, so the return value ofgc.collect()
replaces the old value of_
, andc
finally gets collected. It doesn't have anything to do with the garbage collector; any expression would do: