Классы декораторов в Python - PullRequest
38 голосов
/ 20 марта 2009

Я хочу создать классы для использования в качестве декораторов с соблюдением следующих принципов:

  1. Должно быть возможно сложить несколько таких декораторов классов в функцию top-1.
  2. Полученный указатель имени функции должен быть неотличим от той же самой функции без декоратора, за исключением, возможно, только того, какой это тип / класс.
  3. Заказ декораторов не должен иметь отношения к делу, если это не было на самом деле санкционировано декораторами. То есть. независимые декораторы могут применяться в любом порядке.

Это для проекта Django, и в конкретном случае, над которым я сейчас работаю, метод требует 2 декоратора, и для его отображения в виде обычной функции python:

@AccessCheck
@AutoTemplate
def view(request, item_id) {}

@ AutoTemplate изменяет функцию так, что вместо возврата HttpResponse он просто возвращает словарь для использования в контексте. Используется RequestContext, а имя шаблона выводится из имени метода и модуля.

@ AccessCheck добавляет дополнительные проверки пользователя на основе item_id.

Я предполагаю, что это просто для правильного конструктора и копирования соответствующих атрибутов, но какие это атрибуты?

Следующий декоратор не будет работать, как я описываю:

class NullDecl (object):
    def __init__ (self, func):
        self.func = func
    def __call__ (self, * args):
        return self.func (*args)

Как показывает следующий код:

@NullDecl
@NullDecl
def decorated():
    pass

def pure():
    pass

# results in set(['func_closure', 'func_dict', '__get__', 'func_name',
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals'])
print set(dir(pure)) - set(dir(decorated));

Кроме того, попробуйте добавить «print func. name » в конструктор NullDecl, и он будет работать для первого декоратора, но не для второго - так как имя будет отсутствовать.

Уточненный eduffy ответ немного, и, кажется, работает довольно хорошо:

class NullDecl (object):
    def __init__ (self, func):
        self.func = func
        for n in set(dir(func)) - set(dir(self)):
            setattr(self, n, getattr(func, n))

    def __call__ (self, * args):
        return self.func (*args)
    def __repr__(self):
        return self.func

Ответы [ 3 ]

23 голосов
/ 20 марта 2009

Класс декоратора, который ничего не делает, будет выглядеть так:

class NullDecl (object):
   def __init__ (self, func):
      self.func = func
      for name in set(dir(func)) - set(dir(self)):
        setattr(self, name, getattr(func, name))

   def __call__ (self, *args):
      return self.func (*args)

И тогда вы можете применить его как обычно:

@NullDecl
def myFunc (x,y,z):
   return (x+y)/z
10 голосов
/ 20 марта 2009

Модуль для декоратора помогает вам создавать сохраняющие подпись декораторы.

А PythonDecoratorLibrary может предоставить полезные примеры для декораторов.

7 голосов
/ 20 марта 2009

Чтобы создать декоратор, который оборачивает функции в объекты, которые делают их неотличимыми от исходной функции, используйте functools.wraps.

Пример:


def mydecorator(func):
    @functools.wraps(func):
    def _mydecorator(*args, **kwargs):
        do_something()
        try:
            return func(*args, **kwargs)
        finally:
            clean_up()
    return _mydecorator

# ... and with parameters
def mydecorator(param1, param2):
    def _mydecorator(func):
        @functools.wraps(func)
        def __mydecorator(*args, **kwargs):
            do_something(param1, param2)
            try:
                return func(*args, **kwargs)
            finally:
                clean_up()
        return __mydecorator
    return _mydecorator

(мое личное предпочтение - создавать декораторы с использованием функций, а не классов)

Порядок декораторов следующий:


@d1
@d2
def func():
    pass

# is equivalent to
def func():
    pass

func = d1(d2(func))
...