2011-01-18 22 views
9

Sto cercando un modo per intercettare le chiamate di metodo esempio in classe MyWrapper di seguito:Come intercettare le chiamate al metodo di istanza?

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

voglio intercettare tutte le chiamate di funzione fanno attraverso la mia classe wrapper. Nella mia classe di wrapper voglio tenere traccia di tutte le stringhe dei risultati.

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

risposta

6

Alcuni codice veloce e sporco:

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) 

potrebbe non essere accurata, ma potrebbe essere un punto di partenza decente, immagino.

+0

Grazie mille! Funziona alla grande come volevo! :-) –

+1

Fantastico sapere - prego. Per favore tagga la risposta come "accettata" quindi :) – Shadikka

2

Si potrebbe avvolgere le modalità con decoratori un tempo instanciation:

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

Ora, se si chiama hello o bye, la chiamata passa attraverso log prima:

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

Che cosa si vuole fare è abbastanza simile a this question. Dovresti prendere il tuo codice di esempio nell'ordine inverso, cioè creare una classe per registrare i valori di ritorno delle chiamate al metodo e rendere ereditate da essa le classi che vuoi guardare. che darebbe qualcosa di simile

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' 

Con alcune piccole modifiche, questo metodo permetterebbe anche di registrare i valori restituiti in tutte le istanze RetValWatcher.

Edit: aggiunte modifiche suggerite dal commento di singolarità

Edit2: dimenticato di gestire il caso in cui attr non è (di nuovo thx singolarità) un metodo

+0

+1, mi piace di più questo metodo, ma ho alcune osservazioni: 1) sostituisci 'retvals = []' con 'self.retvals = []', 2) nel caso dell'OP 'x.getFinalResult()' restituirà __za1a2__ not __a1a2__, 3) penso che sia meglio usare 'inspect.ismethod' o' callable (attr) 'invece di' hasattr (attr, '__call __') '. – mouad

+0

il "sé" mancante era solo una svista, ma hai ragione per gli altri 2 punti. modificato;) – MatToufoutu

+0

Ahh scusa ancora; si dimentica 'else: return attr' per il' se callable (attr): 'perché non si desidera oscurare l'attributo call :) – mouad