views:

150

answers:

6

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

+1  A: 

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
Nadia Alramli
+2  A: 

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.

Ants Aasma
I get argument out of range when I try to use the arguments (ie: arg[1] is the first parameter, arg[2] is the second, etc...
Never mind. This works perfectly. I must have typed something wrong. Thanks so much.
+2  A: 

__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)
ASk
+1  A: 

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.

Steve Jessop
A: 

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
Unknown
This "works" so thank you. But it is not exactly what I was hoping for.This would involve me modifying the subclass with "def bar" the __getattribute__ lives in the base class and I would like to be able to create this functionality without changing the derived class.
A: 

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).)

Mark Evans