Prev: example of multi threads
Next: Another MySQL Problem
From: Thomas Jollans on 23 Jun 2010 13:53 On 06/23/2010 02:56 PM, John Reid wrote: > > > Thomas Jollans wrote: >>> 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. > > Now that's a good point! I've added super().__del__ but the problem > remains for some reason. My A class now looks like: > > 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): > super(A, self).__del__() > del self.method > > def method(self): > pass > > > Did you try this? No, I didn't. And, as Peter Otten pointed out, it wouldn't work anyway. But this will work: ############## import sys from functools import wraps def print_exc(func): @wraps(func) def wrapper(*a, **kwa): try: return func(*a, **kwa) except: print 'Exception', sys.exc_info() raise return wrapper class InstanceCounted(object): count = 0 def __init__(self): type(self).count += 1 def __del__(self): type(self).count -= 1 class TestClass(InstanceCounted): def __init__(self): print 'TestClass initialized' super(TestClass, self).__init__() @print_exc def testmethod(self): print "Heureka", self if __name__ == '__main__': print '%d instances!' % TestClass.count TestClass() print '%d instances!' % TestClass.count t = TestClass() print '%d instances!' % TestClass.count t.testmethod() t2 = TestClass() print '%d instances!' % TestClass.count del t2 print '%d instances!' % TestClass.count del t print '%d instances!' % TestClass.count ################ And the output is: 0 instances! TestClass initialized 0 instances! TestClass initialized 1 instances! Heureka <__main__.TestClass object at 0x7f39a6fc2ad0> TestClass initialized 2 instances! 1 instances! 0 instances! > >> >> 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. > > Looking at the original post, I had included an extra "self" in the > argument list > > def decorator(self, *args, **kwds): > > should have been > > def decorator(*args, **kwds): > > > With this correction my method decorates instance methods on objects > successfully although I don't know why the garbage collection isn't > working. > > > >> >> 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. > > Thanks for this. I remember having some problems decorating instance > methods in the past which is why I started doing it as in the original > post. Your method seems just fine though. > > Thanks, > John. > |