It seems as thought getattribute has only 2 parameters (self, name).
However, in the actual code, the method I am intercepting actual takes arguments.
Is there anyway to access those arguments?
Thanks,
Charlie
It seems as thought getattribute has only 2 parameters (self, name).
However, in the actual code, the method I am intercepting actual takes arguments.
Is there anyway to access those arguments?
Thanks,
Charlie
Honestly I'm not sure I understand your question. If you want a way to override getattribute and yet keep the original attributes you can use __dict__
def __getattribute__(self, attr):
if attr in self.__dict__:
return self.__dict__[attr]
# Add your changes here
Method invocation in Python is two step process, first a function is looked up, then it is invoked. For a more involved discussion see my answer to this question.
So you would need to do something like this:
def __getattribute__(self, key):
if key == "something_interesting":
def func(*args, **kwargs):
# use arguments, and possibly the self variable from outer scope
return func
else:
return object.__getattribute__(self, key)
Also, overriding __getattribute__ is usually a bad idea. Because it is called on all attribute accesses it is really easy to end up in an infinite loop, and even if you do everything correctly it ends up being a pretty big performance hit. Are you sure that __getattr__ won't be enough for your purposes? Or maybe even a descriptor object that returns functions. Descriptors are usually a lot better at reuse.
__getattribute__ simply returns the attribute that was requested, in case of a method, the __call__ interface is then used to call it.
Instead of returning the method, return a wrapper around it, for instance:
def __getattribute__(self, attr):
def make_interceptor(callble):
def func(*args, **kwargs):
print args, kwargs
return callble(*args, **kwargs)
return func
att = self.__dict__[attr]
if callable(att):
return make_interceptor(att)
I don't think you can. __getattribute__
doesn't intercept the method call, it only intercepts the method name lookup. So it should return a function (or callable object), which will then be called with whatever parameters specified at the call site.
In particular, if it returns a function which takes (*args, **kwargs)
, then in that function you can examine the arguments however you want.
I think. I'm not a Python expert.
Yes
def argumentViewer(func):
def newfunc(*args, **kwargs):
print "Calling %r with %r %r" % (func, args, kwargs)
return func(*args, **kwargs)
return newfunc
class Foo(object):
def __getattribute__(self, name):
print "retrieving data"
return object.__getattribute__(self, name)
@argumentViewer
def bar(self, a, b):
return a+b
# Output
>>> a=Foo()
>>> a.bar
retrieving data
<bound method Foo.newfunc of <__main__.Foo object at 0x01B0E3D0>>
>>> a.bar(2,5)
retrieving data
Calling <function bar at 0x01B0ACF0> with (<__main__.Foo object at 0x01B0E3D0>, 2, 5) {}
7
Here is a variation on Unknown's suggestion that returns a callable "wrapper" instance in place of the requested function. This does not require decorating any methods:
class F(object):
def __init__(self, func):
self.func = func
return
def __call__(self, *args, **kw):
print "Calling %r:" % self.func
print " args: %r" % (args,)
print " kw: %r" % kw
return self.func(*args,**kw)
class C(object):
def __init__(self):
self.a = 'an attribute that is not callable.'
return
def __getattribute__(self,name):
attr = object.__getattribute__(self,name)
if callable(attr):
# Return a callable object that can examine, and even
# modify the arguments prior to calling the method.
return F(attr)
# Return the reference to any non-callable attribute.
return attr
def m(self, *a, **kw):
print "hello from m!"
return
>>> c=C()
>>> c.a
'an attribute that is not callable.'
>>> c.m
<__main__.F object at 0x63ff30>
>>> c.m(1,2,y=25,z=26)
Calling <bound method C.m of <__main__.C object at 0x63fe90>>:
args: (1, 2)
kw: {'y': 25, 'z': 26}
hello from m!
>>>
Ants Aasma above makes an important point regarding recursion when using __getattribute__
. It is called by all attribute lookups on self
, even those inside of the __getattribute__
method. Several of the other examples include references to self.__dict__
inside of __getattribute__
which will result in recursing until you exceed the maximum stack depth! To avoid this, use one of the
base class' versions of __getattribute__
(e.g. object.__getattribute__(self,name)
.)