2012-02-15 7 views
5

Wystawianie opinii na temat tego, czy jest to uzasadnione, czy też jest lepsze podejście. Zasadniczo chcę dekoratora, który będzie dotyczył funkcji lub klasy, która implementuje __call__.dekorator pytonów dla klasy LUB funkcji

Można po prostu mieć zwykłego dekoratora i dekorować __call__ jawnie, ale wtedy dekorator jest schowany w definicji klasy i jest mniej oczywisty. Być może brakuje mi prostszego rozwiązania.

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) 

Odpowiedz

4

Osobiście, chciałbym podzielić to na dwie dekoratorów: jeden, który zawsze otacza funkcję:

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 

a inny, który wykrywa, czy należy zmodyfikować metodę __call__ lub po prostu zawinąć funkcję:

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 

Pamiętaj, że inspect.isclass zachowa się poprawnie zarówno w klasach starego, jak i w nowym stylu.

1

To całkiem sprytny pomysł. Wydaje mi się, że to w porządku, chociaż może być bardziej pythonic do dekoracji __call__ bezpośrednio, ponieważ "wyraźna jest lepsza niż niejawna". Jest trochę konceptualnego nadmiaru posiadania jednego dekoratora, który robi dwie rzeczy.

(zastanawiam się, czy to byłoby gorzej lub lepiej, aby dekorator że trwa dowolną funkcję dekoratora i zamienia ją w dekoratora dwufunkcyjny/klasa ...)

3

ja nie lubię swojej podejścia. Metoda __call__() jest używana, jeśli wywoływana jest instancja . Wywołanie samej klasy wywołuje zamiast niej __init__(), więc nie uważam tego za analogiczne.

Twój dekorator nie będzie działał dla klas w nowym stylu (bezpośrednio lub pośrednio pochodzących z object). Zrób sobie przysługę i po prostu udekoruj __call__(), jeśli tego chcesz. Albo napisz funkcję fabryczną, która tworzy i udekoruje instancje klasy tej klasy - byłoby to w sumie analogiczne do dekorowania funkcji, ponieważ instancja jest bezpośrednio wywoływalna, a ty nie musisz zepsuć arounf z parametrem self.

+0

Dzięki. (Jeśli ktokolwiek jest zainteresowany, DzinX odnotowuje poprawkę, aby działała z klasami nowego stylu, jest sprawdzenie za pomocą inspect.isclass.) –

Powiązane problemy