【问题标题】:How to intercept instance method calls?如何拦截实例方法调用?
【发布时间】:2011-06-11 01:22:45
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: python function call instance getattr


    【解决方案1】:

    一些快速而肮脏的代码:

    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)
    

    我猜可能不够全面,但可能是一个不错的起点。

    【讨论】:

    • 很高兴知道 - 不客气。请将答案标记为“已接受”然后:)
    【解决方案2】:

    你可以用装饰器包装你的方法一个实例化时间:

    #!/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

    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
    

    【讨论】:

      【解决方案3】:

      你想做的和this question很相似。 您应该以相反的顺序获取示例代码,我的意思是创建一个类来记录方法调用的返回值,并使您想要观看的类继承自它。 哪个会给出这样的结果

      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 getFinalResult(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 不是方法的情况(再次感谢奇点)

      Edit3:修正错字

      【讨论】:

      • +1,我更喜欢这种方法,但我有一些评论:1)用self.retvals = []替换retvals = [],2)在OP的情况下x.getFinalResult()将返回 za1a2 不是 a1a2 ,3) 我认为最好使用inspect.ismethodcallable(attr) 而不是hasattr(attr, '__call__')
      • 缺少的“自我”只是一个疏忽,但你对其他两点是正确的。已编辑;)
      • 啊,再次抱歉;你忘记了if callable(attr):else: return attr,因为你不想隐藏属性调用:)
      • 错字:def getFinalResTTTult(self):
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-11
      • 2011-04-19
      • 2012-04-01
      • 2021-09-16
      • 1970-01-01
      • 2010-11-11
      相关资源
      最近更新 更多