关于python:如何拦截实例方法调用?

How to intercept instance method calls?

我正在寻找一种方法来截获类MyWrapper中的实例方法调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class SomeClass1:
    def a1(self):
        self.internal_z()
        return"a1"
    def a2(self):
        return"a2"
    def internal_z(self):
        return"z"

class SomeClass2(SomeClass1):
    pass

class MyWrapper(SomeClass2):

    # def INTERCEPT_ALL_FUNCTION_CALLS():
    #      result = Call_Original_Function()
    #      self.str += result  
    #      return result  


    def __init__(self):
        self.str = ''
    def getFinalResult(self):
        return self.str

x = MyWrapper()
x.a1()
x.a2()

我想截取通过包装类进行的所有函数调用。在包装类中,我希望跟踪所有结果字符串。

1
2
result = x.getFinalResult()
print result == 'a1a2'


一些快速而肮脏的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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)

可能不是很彻底,但可能是一个不错的起点,我想。


你想做的和这个问题非常相似。您应该按照相反的顺序使用示例代码,我的意思是创建一个类来记录方法调用的返回值,并使您要监视的类从中继承。哪一个会像这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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'

通过一些细微的更改,此方法还允许您记录所有retvalwatcher实例的返回值。

编辑:添加奇点评论建议的更改

edit2:忘记处理attr不是方法的情况(再次出现thx奇点)


你可以用装饰器包装你的方法一个声明时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/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

现在,如果您呼叫hellobye,则呼叫首先经过log

1
2
3
4
5
6
7
8
9
10
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