Классы декораторов в Python
Вопрос
Я хочу создавать классы для использования в качестве декораторов с сохранением следующих принципов:
- Должна быть возможность сложить несколько таких декораторов класса поверх 1 функции.
- Результирующий указатель на имя функции должен быть неотличим от той же функции без декоратора, за исключением, возможно, того, к какому типу / классу он относится.
- Заказ декораторов не должен иметь значения, если только они на самом деле не санкционированы декораторами.Т. е.независимые декораторы могут быть применены в любом порядке.
Это для проекта 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))