Circular reference problem -- advice?

Bjorn Pettersen bjorn at roguewave.com
Mon Jul 10 16:13:53 EDT 2000


Erik Max Francis wrote:
> 
> I'm fairly new to Python, although it's simple enough that I've already
> been able to write some fairly involved programs in it (I already know
> C, C++, Perl, etc.).  This question isn't so much about what's going
> wrong, but rather what's the best, most natural way to get around it.
> 
> I've come across a problem due to circular references, and I'm not sure
> what's the best way to tackle it.  In essence, the problem is that I
> need a particular class to keep an associative array of its methods for
> a lookup table (it's essentially a dispatcher).  But with references to
> its own bound methods, Python's garbage collector concludes it has
> circular references and thus its destructor never gets called.
> 
> Here is a simplified standalone program that demonstrates what I'm
> talking about:
> 
> class C:
>     def __init__(self):
>         print "in constructor"
>         self.map = { 'f': self.f, 'g': self.g }
> 
>     def __del__(self):
>         print "in destructor"
> 
>     def f(self):
>         print "in C.f"
> 
>     def g(self):
>         print "in C.g"
> 
>     def dispatch(self, name):
>         self.map[name]()
> 
> c = C()
> c.dispatch('f')
> 
> When run, this program prints
> 
>     max at charmaine:~/tmp% ./circ.py
>     in constructor
>     in C.f
> 
> and that's it; because of the circular reference, the destructor never
> gets called.
> 
> For the application I need, it's imperative that the destructor get
> called.  What is the best way to solve this?  I can think of a few:
> 
> - Move the map to a local variable in the dispatching function
> - Make map a lookup table of _unbound_ methods
> - Build a string of the function call and then exec it
> 
> What would be the most effective way of getting around this?  I'm
> thinking that using unbound methods would probably be the best way.

It depends on what kind of control you have...  The most straightforward
is probably something like:

class C:
    def __init__(self):
        print "in constructor"
        self.map = { 'f': self.f, 'g': self.g }

    def __del__(self):
        print "in destructor"
    
    def f(self):
        print "in C.f"

    def g(self):
        print "in C.g"

    def dispatch(self, name):
        self.map[name]()
        
    def close(self):
    	del self.map

try:
    c = C()
    c.dispatch('f')
finally:
    c.close()

It would probably be more natural to implement the destructor logic in
the close method with this scheme...

-- bjorn




More information about the Python-list mailing list