Вопрос

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

  1. Должна быть возможность сложить несколько таких декораторов класса поверх 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.Имя" в конструкторе NullDecl, и это будет работать для первого декоратора, но не для второго - поскольку name будет отсутствовать.

Утонченный эдаффивот небольшой ответ, и, похоже, он работает довольно хорошо:

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
Это было полезно?

Решение

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

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

Другие советы

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

И тот Библиотека Pythondecorator для питонов это могло бы послужить полезными примерами для декораторов.

Чтобы создать декоратор, который обертывает функции таким образом, чтобы они были неотличимы от исходной функции, используйте 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))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top