2011-01-18 2 views
9

나는 아래 클래스 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' 

답변

6

빠른 더러운 코드 :

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) 

가 철저하지 않을 수도 있습니다,하지만 괜찮은 출발점이 될 수있는 것 같아요.

+0

덕분에 많은 사건을 처리하는 것을 잊었다! 내가 원했던 것처럼 위대한 작품! :-) –

+1

잘 알고 있습니다 - 여러분 환영합니다. 대답을 "수락"한 것으로 표시하십시오 :) – Shadikka

2

당신은 장식으로 instanciation 시간 당신의 방법을 포장 수 :

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 
2

뭘하고 싶은 것은 : 이제 hello 또는 bye 호출하는 경우

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

를 호출 먼저 log 통과 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 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가 (다시 들으 특이점)하는 방법없는 곳

+0

+1,이 방법은 더 좋아하지만 몇 가지 발언이 있습니다. 1)'retvals = []'를'self.retvals = []', 2)로 바꿉니다. 'x.getFinalResult()'OP의 경우는 __za1a2__이 아닌 __a1a2__을 반환 할 것입니다. 3) hasattr (attr, '__call __') 대신'inspect.ismethod' 또는'callable (attr)'을 사용하는 것이 더 좋습니다. – mouad

+0

누락 된 '자아'는 단지 감시 일 뿐이지 만 다른 2 점에는 맞습니다. 편집 됨;) – MatToufoutu

+0

다시 미안합니다. 애트리뷰트 콜을 애매하게하고 싶지 않기 때문에'if callable (attr) :'에 대해'else : return attr'을 잊어 버렸다. – mouad

관련 문제