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

Это 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 в класс декоратора?

Ответы [ 4 ]

78 голосов
/ 02 марта 2010

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

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

по желанию.

44 голосов
/ 21 июля 2010

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

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

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

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

1 голос
/ 02 марта 2010

Декораторы могут быть немного сложнее в Python. Вот пример того, что я получил на работу некоторое время назад. Из этого вы можете сделать вывод, что вы пытаетесь сделать.

Аутентификация Django и Ajax - URL-адреса, требующие входа в систему

0 голосов
/ 28 июля 2017

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

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...