How to intercept instance method calls?

后端 未结 3 1776
孤街浪徒
孤街浪徒 2020-12-08 11:47

I am looking for a way to intercept instance method calls in class MyWrapper below:

class SomeClass1:
    def a1(self):
        self.internal_z         


        
相关标签:
3条回答
  • 2020-12-08 12:35

    What you want to do is quite similar to this question. You should take your example code in the reverse order, i mean creating a class to record return values of method calls, and make the classes you want to watch inherit from it. Which would give something like this

    class RetValWatcher(object):
        def __init__(self):
            self.retvals = []
    
        def __getattribute__(self, name):
            attr = super(RetValWatcher, self).__getattribute__(name)
            if callable(attr):
                def wrapped(*args, **kwargs):
                    retval = attr(*args, **kwargs)
                    self.retvals.append(retval)
                    return retval
                return wrapped
            else:
                return attr
    
        def getFinalRestult(self):
            return ''.join(self.retvals)
    
    class MyClass(RetValWatcher):
        def a(self):
            self.internal_z()
            return 'a1'
    
        def b(self):
            return 'b1'
    
        def internal_z(self):
            return 'z'
    
    x = MyClass()
    x.a()
    x.b()
    print x.getFinalResult()
    #'za1b1'
    

    With some minor changes, this method would also allow you to record return values across all RetValWatcher instances.

    Edit: added changes suggested by singularity's comment

    Edit2: forgot to handle the case where attr is not a method (thx singularity again)

    0 讨论(0)
  • 2020-12-08 12:36

    You could wrap your methods with decorators a instanciation time:

    #!/usr/bin/env python
    
    import inspect
    
    def log(func):
        def _logged(*args, **kw):
            print "[LD] func", func.__name__, "called with:", args, kw
            result = func(*args, **kw)
            print "[LD] func", func.__name__, "returned:", result
            return result
        return _logged
    
    class A(object):
        def __init__(self):
            for x in inspect.getmembers(self, (inspect.ismethod)):
                if not x[0].startswith('__'):
                    setattr(self, x[0], log(getattr(self, x[0])))
    
        def hello(self):
            print "Hello"
    
        def bye(self):
            print "Bye"
            return 0
    

    Now if you call hello or bye, the call goes through log first:

    a = A()
    a.hello()
    a.bye()
    
    # [LD] func hello called with: () {}
    # Hello
    # [LD] func hello returned: None
    # [LD] func bye called with: () {}
    # Bye
    # [LD] func bye returned: 0
    
    0 讨论(0)
  • 2020-12-08 12:40

    Some quick and dirty code:

    class Wrapper:
        def __init__(self, obj):
            self.obj = obj
            self.callable_results = []
    
        def __getattr__(self, attr):
            print("Getting {0}.{1}".format(type(self.obj).__name__, attr))
            ret = getattr(self.obj, attr)
            if hasattr(ret, "__call__"):
                return self.FunctionWrapper(self, ret)
            return ret
    
        class FunctionWrapper:
            def __init__(self, parent, callable):
                self.parent = parent
                self.callable = callable
    
            def __call__(self, *args, **kwargs):
                print("Calling {0}.{1}".format(
                      type(self.parent.obj).__name__, self.callable.__name__))
                ret = self.callable(*args, **kwargs)
                self.parent.callable_results.append(ret)
                return ret
    
    class A:
        def __init__(self, val): self.val = val
        def getval(self): return self.val
    
    w = Wrapper(A(10))
    print(w.val)
    w.getval()
    print(w.callable_results)
    

    Might not be thorough, but could be a decent starting point, I guess.

    0 讨论(0)
提交回复
热议问题