-1

I'm trying to print out the function's name and arguments that is called by the instance everytime the instance makes a function call

I'm using getattribute method to implement this, my code is below

    def __getattribute__(self, attr):

        def newfunc(*args, **kwargs):

            print( "%r Calling %r with %r %r" % (self, attr, args, kwargs))


    return newfunc 

The code does print out the function's name and arguments, but the origin method is not executed, since it returns a new function instead of calling the old one.

I did some searchs, like this question, the highest vote in this answer using dict to retrieve the function by it's name in a dictionary, I tried that, but it result in a recursive call.

My question is. is there a way to print out function's name and arguments inside __getattribute__method ?

or is there a way to print out function's name and arguments everytime an instance is calling its method?


Ps. except using decorators, I knew I can use decorator for this, but I don't want to put decorator in front of every method I use

jsbueno
  • 99,910
  • 10
  • 151
  • 209
whtitefall
  • 631
  • 9
  • 16

2 Answers2

0

The main, and obvious, problem with your code is that at no point you retrieve the original attribute in the instance, which is the job of __getattribute__ - breaking __getattribute__ is a 100% dead certan way of making your class not work at all.

So, the first thing is to call a working __getattribute__ on a superclass to retrieve the attribute.

Then the second part outstandingly lacking in your code is that you don't even try to call the original function or method from your "printer" function. We put such a call at the end of its code.

That done, a bit more care, like, do not try to return non-callable attributes wrapped in a function, because that would fail.

After taking care of these 3 points, it should work as you have intended:

def __getattribute__(self, attr):

    original = super().__getattribute__(attr)
    if callable(original):
        def newfunc(*args,  **kwargs):

            print( "%r Calling %r with %r %r" % (self, attr, args, kwargs))
            return original(*args, **kwargs)
        return newfunc
    return original

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • Thanks ! it works for me, I actually tried to call the function using getattr, but it didn't work – whtitefall Oct 16 '19 at 13:54
  • `__getattr__` is only called for attributes that are not found on the instance - its intent is to provide dynamic attributes that it alone exposes. In contrst, `__getattribute__` is called for _every_ attribute access. (and `__getattr__` itself is called inside `object.__getattribute__` as part of attribute lookup, not after it returns) – jsbueno Oct 16 '19 at 14:15
0

As you don't want to apply decorator but still need to "print out function's name and arguments inside __getattribute__ method" - use the following approach:

In order to avoid infinite recursion in __getattribute__ method, its implementation should always call the base class method with the same name to access any attributes it needs.

class A:
    def my_func(self, a, b):
        return a + b

    def __getattribute__(self, attr):
        def newfunc(*args, **kwargs):
            print("%r Calling %r with %r %r" % (self, attr, args, kwargs))

            return object.__getattribute__(self, attr)
        return newfunc

a = A()
a.my_func(1, 2)
a.my_func(3, 4)

Sample output:

<__main__.A object at 0x116861710> Calling 'my_func' with (1, 2) {}
<__main__.A object at 0x116861710> Calling 'my_func' with (3, 4) {}
RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105