2011-01-18 4 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

Вы можете обернуть свои методы с декораторами времени при создании экземпляра:

#!/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, вызов проходит через 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 
2

То, что вы хотите сделать, это довольно похоже на 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: забыли обработать случай, когда атр не метод (ТНХ особенность снова)

+0

+1, мне нравится этот метод больше, но у меня есть некоторые замечания: 1) заменить 'retvals = []' на 'self.retvals = []', 2) в случай OP 'x.getFinalResult()' будет возвращать __za1a2__ not __a1a2__, 3), я думаю, что лучше использовать 'inspect.ismethod' или' callable (attr) 'вместо' hasattr (attr, '__call __') '. – mouad

+0

недостающее «я» было просто надзором, но вы правы для остальных 2 очков. отредактировано;) – MatToufoutu

+0

Ах снова жалко; вы забыли 'else: return attr' для' if callable (attr): ', потому что вы не хотите скрывать вызов атрибута :) – mouad

Смежные вопросы