From: Thomas Jollans on
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.
>

First  |  Prev  | 
Pages: 1 2
Prev: example of multi threads
Next: Another MySQL Problem