2011-03-30 9 views
9

decir que tengo una clase:Python añadir dinámicamente decorador a la clase métodos mediante la decoración de la clase

class x: 

    def first_x_method(self): 
     print 'doing first_x_method stuff...' 

    def second_x_method(self): 
     print 'doing second_x_method stuff...' 

y este decorador

class logger: 
    @staticmethod 
    def log(func): 
     def wrapped(*args, **kwargs): 
      try: 
       print "Entering: [%s] with parameters %s" % (func.__name__, args) 
       try: 
        return func(*args, **kwargs) 
       except Exception, e: 
        print 'Exception in %s : %s' % (func.__name__, e) 
      finally: 
       print "Exiting: [%s]" % func.__name__ 
     return wrapped 

¿cómo voy a escribir otro decorador otherdecorator de modo que:

@otherdecorator(logger.log) 
class x: 

    def first_x_method(self): 
     print 'doing x_method stuff...' 

    def first_x_method(self): 
     print 'doing x_method stuff...' 

lo mismo que

class x: 
     @logger.log 
     def first_x_method(self): 
      print 'doing first_x_method stuff...' 

     @logger.log 
     def second_x_method(self): 
     print 'doing second_x_method stuff...' 

hecho o de reemplazar

@otherdecorator(logger.log) 
class x: 

con

@otherdecorator 
class x: 

donde otherdecorator contiene toda la funcionalidad (no soy una persona pitón así que se gentil)

+0

¿Qué versión de Python estás utilizando? – detly

+0

2.6 y Iron Python (clr 4.0/dlr) –

Respuesta

17

A menos que haya una razón definitiva para usar una clase como decorador, creo que generalmente es más fácil usar funciones para definir decoradores.

Aquí es una manera de crear un decorador de clase trace, que decora todos los métodos de una clase con la log decorador:

import inspect 

def log(func): 
    def wrapped(*args, **kwargs): 
     try: 
      print "Entering: [%s] with parameters %s" % (func.__name__, args) 
      try: 
       return func(*args, **kwargs) 
      except Exception, e: 
       print 'Exception in %s : %s' % (func.__name__, e) 
     finally: 
      print "Exiting: [%s]" % func.__name__ 
    return wrapped 

def trace(cls): 
    for name, m in inspect.getmembers(cls, inspect.ismethod): 
     setattr(cls,name,log(m)) 
    return cls 

@trace 
class X(object): 
    def first_x_method(self): 
     print 'doing first_x_method stuff...' 
    def second_x_method(self): 
     print 'doing second_x_method stuff...' 

x=X() 
x.first_x_method() 
x.second_x_method() 

se obtiene:

Entering: [first_x_method] with parameters (<__main__.X object at 0xb77c80ac>,) 
doing first_x_method stuff... 
Exiting: [first_x_method] 
Entering: [second_x_method] with parameters (<__main__.X object at 0xb77c80ac>,) 
doing second_x_method stuff... 
Exiting: [second_x_method] 
+0

impresionante. Muchas gracias. –

+0

¿Entonces, presumiblemente, podría agregar el decorador en tiempo de ejecución con un settattr en el tipo para secar? –

+1

@Preet Sangha: Sí, no creo que haya ningún problema, excepto que las instancias de la clase realizadas antes de que se aplique el decorador no se modificarán. – unutbu

2

Aquí hay una versión de la trace decorador implementado como una clase que permite el otro caso de uso solicitado: pasar la función para decorar todas las funciones miembro de la clase decorada con.

import inspect 


def log(func): 
    def wrapped(*args, **kwargs): 
     try: 
      print "Entering: [%s] with parameters %s" % (func.__name__, args) 
      try: 
       return func(*args, **kwargs) 
      except Exception, e: 
       print 'Exception in %s : %s' % (func.__name__, e) 
     finally: 
      print "Exiting: [%s]" % func.__name__ 
    return wrapped 


class trace(object): 

    def __init__(self, f): 
     self.f = f 

    def __call__(self, cls): 
     for name, m in inspect.getmembers(cls, inspect.ismethod): 
      setattr(cls, name, self.f(m)) 
     return cls 


@trace(log) 
class X(object): 

    def first_x_method(self): 
     print 'doing first_x_method stuff...' 

    def second_x_method(self): 
     print 'doing second_x_method stuff...' 

x = X() 
x.first_x_method() 
x.second_x_method() 
+0

Bueno. Gracias. –

Cuestiones relacionadas