Problem with garbage collection (sort of)

Tim Evans t.evans at paradise.net.nz
Tue Aug 19 04:23:45 EDT 2003


frank at chagford.com (Frank Millman) writes:
>snip<
> class a:
>     def __init__(self,b):
>         self.b = b
>         print 'a created'
>     def __del__(self):
>         print 'a deleted'
>  
> class b:
>     def __init__(self):
>         self.p = []
>         e = a(self)
>         self.p.append(e)
>         print 'b created'
>     def __del__(self):
>         print 'b deleted'
> 
> class c:
>     def __init__(self):
>         self.f = b()
>         print 'c created'
>     def __del__(self):
> #       del self.f.p
>         print 'c deleted'
> 
> y = c()
> #del y.f.p
> 
> #--------------------------------------------
> 
> y is an instance of class c, which creates an instance of class b,
> which creates an instance of class a. When y goes out of scope and is
> deleted, I want the instances of class b and class a to be deleted as
> well.
> 
> The problem is that class a keeps a reference to class b (self.b) and
> class b keeps a reference to class a (self.p), so the reference counts
> do not go down to zero without some additional action.
> 
> I have 2 possible solutions - the two commented-out lines. If either
> of these lines are uncommented, all instances are deleted.
> 
> The ugliness is that p is internal to class b - neither class c nor
> the main application requires any knowledge of it. However, my
> solution requires one or the other to explicitly delete it.
> 
> Any advice will be much appreciated.
> 
> Many thanks
> 
> Frank Millman

Others have discussed the cyclic garbage collector...

Another possible solution is to use the weakref module.  See the docs
for the details, but this here is how you could use it:

---------------------------------------
import weakref

class a:
    def __init__(self,b):
        self.b = weakref.ref(b)
        print 'a created'
    def __del__(self):
        print 'a deleted'
---------------------------------------

The only difference is that when you access 'self.b' you need to use
'self.b()' to follow the weakref and get the real object.  Note that
'self.b()' will return None if the real object has been destroyed
already (i.e. when nothing references it with a real reference).  Weak
refs are quite useful in the situation that you appear to have, where
a node in a tree needs to access it's parent but where the node won't
outlive it's parent.

You're still probably better off trusting the cyclic garbage
collector, but weakrefs are another option.

Note that if you want to watch things being collected by the gc, you
can do this:

---------------------------------------
class DelWatcher:
    def __init__(self, obj):
        self.objrepr = repr(obj)
    def __del__(self):
        print '%s deleted' % self.objrepr

class a:
    def __init__(self,b):
        self._delwatcher = DelWatcher(self)
        self.b = weakref.ref(b)
        print 'a created'
---------------------------------------

You may need to do 'import gc; gc.collect()' to actually collect all
the objects right away.  'gc.collect()' gets run periodically by the
interpreter so you don't need to do it in your application.

-- 
Tim Evans





More information about the Python-list mailing list