Instance method decorator garbage collection problem

Thomas Jollans thomas at jollans.com
Wed Jun 23 08:27:31 EDT 2010


On 06/23/2010 01:40 PM, 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.

Adding __del__ like this does "fix the problem", but it introduces a new
one: lacking a call to super().__del__, you simply don't decrement the
instance count.

To decorate a method, you'd best just decorate it normally. I doubt your
technique will work anyway, as the function returned by the decorator
isn't bound to the object, you'd need to pass one self reference
implicitly, which is then thrown away.

simply,

def exc_decor(fn):
    @functools.wraps(fn)
    def wrapper(*args, **keywords):
        try:
            return fn(*args, **keywords):
        except:
            #...
            raise
    return wrapper

class X(...):
   @exc_decor
   def foo(self, arg):
      pass

(if targeting pre-decorator Python, the code would look different of course)

This way, the function itself is decorated, and the function returned by
the decorator is bound to the object. It'll just work as expected, no
trickery required.

-- Thomas

> 
> Can anyone suggest anything? Is my technique to decorate bound methods
> not a good one? How else should I decorate a bound method?
> 
> Thanks in advance,
> John.
> 
> 




More information about the Python-list mailing list