John Reid wrote: > Hi, > > I've written a decorator that prints exceptions and I'm having some > trouble with garbage collection. > > My decorator is: > > import sys > def print_exception_decorator(fn): > def decorator(self, *args, **kwds): > try: > return fn(*args, **kwds) > except: > print 'Exception:', sys.exc_info() > raise > return decorator > > > > The class I want to decorate the methods of is: > > class InstanceCounted(object): > "A class that keeps track of how many instances there are." > count = 0 > def __init__(self): > InstanceCounted.count += 1 > def __del__(self): > InstanceCounted.count -= 1 > > class A(InstanceCounted): > "A class that I want to decorate a method on." > def __init__(self): > super(A, self).__init__() > self.method = print_exception_decorator(self.method) > > def __del__(self): > del self.method > > def method(self): > pass > > > > When I run the following it does not seem like my object 'a' is garbage > collected: > > print 'Have %d instances' % InstanceCounted.count > print 'Creating A' > a = A() > print 'Have %d instances' % InstanceCounted.count > print 'Deleting A' > del a > print 'Have %d instances' % InstanceCounted.count > > > This is the output: > > Have 0 instances > Creating A > Have 1 instances > Deleting A > Have 1 instances > > > The InstanceCounted.count is 1 at the end. If I omit the call to > "self.method = print_exception_decorator(self.method)" then the instance > count goes down to 0 as desired. I thought that the decorator might be > holding a reference to the instance through the bound method, so I added > the __del__() but it doesn't fix the problem. > > Can anyone suggest anything? Is my technique to decorate bound methods > not a good one? How else should I decorate a bound method?
The problem is that cyclic garbage collection cannot cope with __del__() methods. Quoting http://docs.python.org/library/gc.html#gc.garbage """ Objects that have __del__() methods and are part of a reference cycle cause the entire reference cycle to be uncollectable """ The best workaround is to control the object's lifetime explicitly by turning it into a context manager, see http://docs.python.org/reference/datamodel.html#with-statement-context- managers or providing a close() method and use contextlib: >>> class A(object): ... def close(self): ... print "break cycles here" ... >>> class A(object): ... def __init__(self): ... self.self = self ... def __del__(self): ... print "bye" ... def close(self): ... print "breaking cycles here" ... del self.self ... >>> a = A() >>> del a >>> import contextlib >>> with contextlib.closing(A()) as b: ... print "using b" ... using b breaking cycles here >>> del b bye Peter -- http://mail.python.org/mailman/listinfo/python-list