a small doubt

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Sun Dec 21 15:17:45 CET 2008


(answering to the OP)

> Piyush Anonymous wrote:
>> i wrote this code
>> -- 
>> class Person(object):
>>     instancesCount = 0
>>     def __init__(self, title=""):
>>         Person.instancesCount += 1
>>         self.id <http://self.id> = "tempst"
>>     def testprint(self):
>>         print "blah blah"
>>     def __getattribute__(self, name):
>>         print "in get attribute"
>>
>> a = Person()
>> a.testprint()
>> print a.id <http://a.id>
>> -----
>> but i am getting this error
>> ----
>> in get attribute
>> Traceback (most recent call last):
>>   File "trapmethodcall1.py", line 15, in <module>
>>     a.testprint()
>> TypeError: 'NoneType' object is not callable
>> ------
>> why is it so? __getattribute__ is called whenever an attribute or 
>> method is called, rt?


Yes. Methods are attributes too.  And __getattribute__ is the attribute 
lookup operator implementation - it is invoked *everytime* you have 
obj.attr or getattr(obj, "attr") or hasattr(obj, "attr"). IOW, you'd 
better *not* touch it unless 1/ you really know what you're doing, 2/ 
you have a very compelling reason to do so and 3/ you're ok to pay the 
performance hit (the default implementation in 'object' being optimized).


>> or if i set __getattribute__ , i cannot have 
>> methods in class?

Yes, you can. But you have to write a proper implementation of 
__getattribute__. The one above will prevent you from accessing any 
class or instance attribute (or, more exactly, will always return None).

>> actually my aim is to trap all method calls and keep a counter which 
>> is update whenever method called or returned. how should i go about it?


Using __getattribute__, you'll only get at the method when it is looked 
up (which doesn't imply it will be called). If you want to log calls and 
returns, you'll have to wrap the method in a decorator. You can do this 
either manually (ie manually adding the logger decorator on methods), 
via monkeypatching on class objects (but this can be tricky too), or 
adding a properly implemented version of __getattribute__ (but this 
won't work if the method is looked up on the class itself)

# a Q&D method call logger

def log(meth):
     if hasattr(meth, "logged"):
         # already wrapped
         return meth

     def logged(*args, **kw):
         if hasattr(meth, "im_self"):
             # wrapping a method
             what = "method %s " % meth
         else:
             # wrapping a function, 'self' or 'cls' is the first arg
             what = "method %s of object %s" % (meth, args[0])
         print "%s called with %s %s" % (what, str(args), kw)
         try:
             result = meth(*args, **kw)
         except Exception, e:
             print "%s raised %s" % (what, e)
             raise
         else:
             print "%s returned %s" % (what, str(result))
         return result
     logged.logged = True
     return logged


# manual decoration

class Foo(object):
     @log
     def bar(self):
         pass


# using __getattribute__

class Foo(object):
     def bar(self):
         pass
     def __getattribute__(self, name):
         attr = super(Foo2, self).__getattribute__(name)
         if hasattr(attr, "im_self"):
             attr = log(attr)
         return attr



Given the shortcomings of the __getattribute__ version (performance hit 
and only triggered on instance lookup), I'd strongly suggest the manual 
decoration - possibly using a flag (global setting or environment 
variable) to switch it off, ie:

if globals().get("DEBUG", False):
     def log(func):
         def logged(*args, **kw):
             # wrapping a function
             # we assume this function is used as a method, so
             # 'self' or 'cls' is the first arg
             what = "method %s of object %s" % (func, args[0])
             print "%s called with %s %s" % (what, str(args), kw)
             try:
                 result = func(*args, **kw)
             except Exception, e:
                 print "%s raised %s" % (what, e)
                 raise
             print "%s returned %s" % (what, str(result))
             return result
     return logged
else:
     def log(func):
         # no-op
         return func

NB : all this is pretty Q&D and mostly untested, but it should get you 
started.


HTH



More information about the Python-list mailing list