복붙노트

[PYTHON] 인스턴스 메소드 호출을 인터셉트하는 방법은 무엇입니까?

PYTHON

인스턴스 메소드 호출을 인터셉트하는 방법은 무엇입니까?

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'

해결법

  1. ==============================

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

    2.당신이하고 싶은 것은이 질문과 아주 비슷합니다. 역순으로 예제 코드를 가져와야합니다. 즉, 메서드 호출의 반환 값을 기록하는 클래스를 만들고,보고 싶은 클래스를 그 클래스에서 상속 받도록 만듭니다.  어떤 것을 줄 것인가?

    당신이하고 싶은 것은이 질문과 아주 비슷합니다. 역순으로 예제 코드를 가져와야합니다. 즉, 메서드 호출의 반환 값을 기록하는 클래스를 만들고,보고 싶은 클래스를 그 클래스에서 상속 받도록 만듭니다.  어떤 것을 줄 것인가?

    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 인스턴스에 반환 값을 기록 할 수도 있습니다.

    편집 : singularity의 의견에 의해 제안 된 변경 사항 추가

    Edit2 : attr이 메서드가 아닌 경우를 처리하는 것을 잊었습니다 (다시 thx singularity).

  3. ==============================

    3.당신은 꾸미기로 당신의 방법을 감쌀 수있다 :

    당신은 꾸미기로 당신의 방법을 감쌀 수있다 :

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

    이제 hello 나 bye를 호출하면 통화가 먼저 로그를 통과합니다.

    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
    
  4. from https://stackoverflow.com/questions/4723717/how-to-intercept-instance-method-calls by cc-by-sa and MIT license