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