Reliably call code after object no longer exists or is "unreachable"?
Jack Bates
ms419 at freezone.co.uk
Wed Apr 27 12:48:42 EDT 2011
In Python, how can you reliably call code - but wait until an object no
longer exists or is "unreachable"?
I want to ensure that some code is called (excluding some exotic
situations like when the program is killed by a signal not handled by
Python) but can't call it immediately. I want to wait until there are no
references to an object - or the only references to the object are from
unreachable reference cycles
#!/usr/bin/env python
class Goodbye:
def __del__(self):
print 'Goodbye, world!'
ref = Goodbye()
$ ./goodbye
Goodbye, world!
$
Python's __del__ or destructor method works (above) - but only in the
absence of reference cycles (below). An object, with a __del__ method,
in a reference cycle, causes all objects in the cycle to be
"uncollectable". This can cause memory leaks and because the object is
never collected, its __del__ method is never called
> Circular references which are garbage are detected when the option
> cycle detector is enabled (it's on by default), but can only be
> cleaned up if there are no Python-level __del__() methods involved.
#!/usr/bin/env python
class Goodbye:
def __del__(self):
print 'Goodbye, world!'
class Cycle:
def __init__(self, cycle):
self.next = cycle
cycle.next = self
Cycle(Goodbye())
$ ./cycle
$
In PEP 342 I read that an object, with a __del__ method, referenced by a
cycle but not itself participating in the cycle, doesn't cause objects
to be uncollectable. If the cycle is "collectable" then when it's
eventually collected by the garbage collector, the __del__ method is
called
> If the generator object participates in a cycle, g.__del__() may not
> be called. This is the behavior of CPython's current garbage
> collector. The reason for the restriction is that the GC code needs to
> "break" a cycle at an arbitrary point in order to collect it, and from
> then on no Python code should be allowed to see the objects that
> formed the cycle, as they may be in an invalid state. Objects "hanging
> off" a cycle are not subject to this restriction.
#!/usr/bin/env python
import sys
class Destruct:
def __init__(self, callback):
self.__del__ = callback
class Goodbye:
def __init__(self):
self.destruct = Destruct(lambda: sys.stdout.write('Goodbye, world!\n'))
class Cycle:
def __init__(self, cycle):
self.next = cycle
cycle.next = self
Cycle(Goodbye())
$ ./dangle
Goodbye, world!
$
However it's *extremely* tricky to ensure that the object with a __del__
method doesn't participate in a cycle, e.g. in the example below, the
__del__ method is never called - I suspect because the object with a
__del__ method is reachable from the global scope, and this forms a
cycle with a frame's f_globals reference? "storing a generator object in
a global variable creates a cycle via the generator frame's f_globals
pointer"
#!/usr/bin/env python
import sys
class Destruct:
def __init__(self, callback):
self.__del__ = callback
class Goodbye:
def __init__(self):
self.destruct = Destruct(lambda: sys.stdout.write('Goodbye, world!\n'))
class Cycle:
def __init__(self, cycle):
self.next = cycle
cycle.next = self
ref = Cycle(Goodbye())
$ ./global
$
Faced with the real potential for reference cycles, how can you reliably
call code - but wait until an object no longer exists or is
"unreachable"?
More information about the Python-list
mailing list