2012-02-15 11 views
5

Simplemente solicitando la opinión sobre si lo siguiente es razonable o si hay un mejor enfoque. Básicamente, quiero un decorador que se aplique a una función o una clase que implemente __call__.decorador de pitones para la clase O función

Puede simplemente tener un decorador regular y decorar el __call__ de forma explícita, pero luego el decorador está metido dentro de la definición de clase y es menos obvio. Tal vez me falta una solución más directa.

import types 
from functools import wraps 

class dec: 
    """ Decorates either a class that implements __call__ 
     or a function directly. 
    """ 
    def __init__(self, foo): 
     self._foo = foo 

    def __call__(self, target): 
     wraps_class = isinstance(target, types.ClassType) 
     if wraps_class: 
      fun = target.__call__ 
     else: 
      fun = target 

     @wraps(fun) 
     def bar(*args, **kwds): 
      val = args[1] if wraps_class else args[0] 
      print self._foo, val 
      return fun(*args, **kwds) 
     if wraps_class: 
      target.__call__ = bar 
      return target 
     else: 
      return bar 

@dec('A') 
class a: 
    # you could decorate here, but it seems a bit hidden 
    def __call__(self, val): 
     print "passed to a:", val 

@dec('B') 
def b(val): 
    print "passed to b:", val 

a()(11) 
b(22) 

Respuesta

4

Personalmente, me gustaría dividirla en dos decoradores: una que siempre envuelve una función:

def func_dec(foo, is_method=False): 
    def wrapper(fun): 
     @wraps(fun) 
     def bar(*args, **kwds): 
      val = args[1] if is_method else args[0] 
      print foo, val 
      return fun(*args, **kwds) 
     return bar 
    return wrapper 

y otro que detecta si se debe modificar un método __call__ o simplemente envolver una función:

def dec(foo): 
    def wrapper(obj): 
     if inspect.isclass(obj): 
      obj.__call__ = func_dec(foo, is_method=True)(obj.__call__) 
      return obj 
     else: 
      return func_dec(foo)(obj) 
    return wrapper 

Tenga en cuenta que inspect.isclass se comportará correctamente con las clases de estilo antiguo y nuevo.

1

Esa es una bonita idea astuta. Me parece bien, aunque podría ser más pitónico decorar __call__ directamente ya que "explícita es mejor que implícita". Hay un poco de sobrecarga conceptual para tener un decorador que hace dos cosas.

(Me pregunto si sería mejor o peor para hacer un decorador que toma cualquier decorador de la función y lo convierte en un decorador doble función/clase ...)

3

que no me gusta mucho su enfoque. El método __call__() se usa si se llama a una instancia . Llamar a la clase en sí misma invoca el __init__(), por lo que no veo que esto sea realmente análogo.

Su decorador no funcionará para las clases de estilo nuevo (derivadas directa o indirectamente de object). Hazte un favor y simplemente decora __call__() si esto es lo que quieres. O escriba una función de fábrica que cree y decore las instancias de la clase; esto sería en total analogía con la decoración de una función, porque la instancia es directamente invocable y no tiene que entrometerse con el parámetro self.

+0

Gracias. (Y si alguien está interesado, DzinX observa que la solución para hacerlo funcionar con las clases de estilo nuevo es verificar con inspect.isclass). –

Cuestiones relacionadas