2011-01-18 9 views
9

Busco una manera de interceptar las llamadas de método instancia de la clase MyWrapper a continuación:¿Cómo se interceptan las llamadas al método de instancia?

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

Quiero interceptar todas las llamadas a funciones hacen a través de mi clase contenedora. En mi clase de contenedor, quiero hacer un seguimiento de todas las cadenas de resultados.

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

Respuesta

6

Algunos rápida y sucia código:

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) 

podría no ser exhaustiva, pero podría ser un punto de partida decente, supongo.

+0

Muchas gracias! ¡Está funcionando genial como yo quería! :-) –

+1

Genial saberlo - de nada. Por favor marque la respuesta como "aceptada" luego :) – Shadikka

2

Se puede envolver sus métodos con los decoradores de un momento 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 

Ahora bien, si se llama a hello o bye, la llamada se procesa log primera:

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

Lo que se quiere hacer es bastante similar a this question. Debe tomar su código de ejemplo en el orden inverso, me refiero a crear una clase para registrar los valores devueltos de las llamadas a métodos, y hacer que las clases que desea ver hereden de ella. lo que daría algo como esto

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 algunos cambios menores, este método también permitirá grabar los valores de retorno en todos los casos RetValWatcher.

Edit: añadido cambios sugeridos por el comentario de singularidad

Edit2: olvidaron de manejar el caso en que attr no es un método (singularidad THX de nuevo)

+0

+1, me gusta más este método, pero tengo algunas observaciones: 1) reemplace 'retvals = []' con 'self.retvals = []', 2) en el caso del OP 'x.getFinalResult()' devolverá __za1a2__ not __a1a2__, 3) creo que es mejor usar 'inspect.ismethod' o 'invocable (attr)' en lugar de 'hasattr (attr,' __call __ ')'. – mouad

+0

el 'yo' que faltaba era solo un descuido, pero tienes razón para los otros 2 puntos. editado;) – MatToufoutu

+0

Ahh lo siento de nuevo; Olvidas 'else: return attr' for the' if callable (attr): 'porque no deseas ocultar la llamada de atributo :) – mouad

Cuestiones relacionadas