Dekorowanie metod klas Pythona - jak przekazać instancję dekoratorowi?

To jest Python 2.5, i to jest GAE też, nie żeby to miało znaczenie.

Mam następujący kod. Dekoruję metodę foo () w bar, używając klasy dec_check jako dekoratora.

class dec_check(object):

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

  def __call__(self):
    print 'In dec_check.__init__()'
    self.func()

class bar(object):

  @dec_check
  def foo(self):
    print 'In bar.foo()'

b = bar()
b.foo()

Podczas wykonywania tego miałem nadzieję zobaczyć:

In dec_check.__init__()
In bar.foo()

Ale dostaję "TypeError: foo() takes exactly 1 argument (0 given) " jako .foo(), będąc metodą obiektową, przyjmuje jaźń jako argument. Domyślam się, że problem polega na tym, że instancja bar nie istnieje, gdy wykonuję dekoratora kod.

Jak przekazać instancję bar do klasy dekoratorów?

Author: ShadowRanger, 2010-03-02

3 answers

Musisz przekształcić dekorator w deskryptor -- albo przez upewnienie się, że jego (meta)klasa ma metodę __get__, albo sposób prostszy, używając dekoratora Funkcja zamiast dekoratora klasa (ponieważ funkcje są już deskryptorami). Np.:

def dec_check(f):
  def deco(self):
    print 'In deco'
    f(self)
  return deco

class bar(object):
  @dec_check
  def foo(self):
    print 'in bar.foo'

b = bar()
b.foo()

To wydruki

In deco
in bar.foo

Zgodnie z życzeniem.

 90
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-05 19:54:42

Odpowiedź Alexa wystarcza, gdy funkcja jest wystarczająca. Jednak, gdy potrzebujesz klasy, możesz sprawić, że będzie działać, dodając następującą metodę do klasy dekoratora.

def __get__(self, obj, objtype):
    """Support instance methods."""
    import functools
    return functools.partial(self.__call__, obj)

Aby to zrozumieć, musisz zrozumieć protokół deskryptora. Protokół deskryptora jest mechanizmem wiązania rzeczy z instancją. Składa się z __get__, __set__ i __delete__, które są wywoływane, gdy rzecz jest pobrana, ustawiona lub usunięta ze słownika instancji.

W tym przypadku, gdy rzecz ma z instancji wiążemy pierwszy argument jej metody __call__ z instancją, używając częściowego. Jest to wykonywane automatycznie dla funkcji Członkowskich, gdy klasa jest konstruowana, ale dla syntetycznej funkcji member, takiej jak ta, musimy to zrobić jawnie.

 49
Author: Gordon Wrigley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-08 17:20:03

Jeśli chcesz napisać dekorator jako klasę możesz to zrobić:

from functools import update_wrapper, partial

class MyDecorator(object):
    def __init__(self, func):
        update_wrapper(self, func)
        self.func = func

    def __get__(self, obj, objtype):
        """Support instance methods."""
        return functools.partial(self.__call__, obj)

    def __call__(self, obj, *args, **kwargs):
        print('Logic here')
        return self.func(obj, *args, **kwargs)

my_decorator = MyDecorator

class MyClass(object):
     @my_decorator
     def my_method(self):
         pass
 3
Author: Thomas Turner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-20 12:23:53