Подтвердить что ты не робот

Украшение методов класса Python - как передать экземпляр декоратору?

Это Python 2.5, и он GAE тоже, но это не так важно.

У меня есть следующий код. Я украшаю метод foo() в баре, используя класс dec_check в качестве декоратора.

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

При выполнении этого я надеялся увидеть:

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

Но я получаю "TypeError: foo() takes exactly 1 argument (0 given)", поскольку .foo(), будучи методом объекта, принимает в качестве аргумента self. Я предполагаю, что проблема в том, что экземпляр bar на самом деле не существует, когда я выполняю код декоратора.

Итак, как мне передать экземпляр bar в класс декоратора?

4b9b3361

Ответ 1

Вам нужно превратить декоратор в дескриптор - либо убедившись, что его (мета) класс имеет метод __get__, либо, способ проще, используя функцию декоратора вместо класс декоратора (поскольку функции уже являются дескрипторами). Например.:

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

это печатает

In deco
in bar.foo

по желанию.

Ответ 2

Алекс ответа достаточно, когда функции достаточно. Однако когда вам нужен класс, вы можете заставить его работать, добавив следующий метод в класс декоратора.

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

Чтобы понять это, вам нужно понять протокол дескриптора. Протокол дескриптора - это механизм для привязки объекта к экземпляру. Он состоит из __ get __, __set __ и __ delete __, которые вызываются при получении, установке или удалении объекта из словаря экземпляров.

В этом случае, когда вещь получается из экземпляра, мы привязываем первый аргумент его метода __call__ к экземпляру, используя частичное. Это делается автоматически для функций-членов, когда класс создается, но для синтетической функции-члена, подобной этой, нам нужно сделать это явно.

Ответ 4

Если вы хотите написать декоратор как класс, вы можете сделать:

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