Пара слов о декораторах
Всем известна одна особенность декораторов - если просто, без всяких ухищрений,
написать декоратор и применить его к функции, то любые способы определить имя
функции, посмотреть её документацию и т.д. окажутся бесполезными - декоратор их
заменяет своими. Для примера возьмём таки начавший понемногу расходиться по
проектам render_to:
def render_to(tmpl):
def renderer(func):
def wrapper(request, *args, **kw):
output = func(request, *args, **kw)
if not isinstance(output, dict):
return output
return render_to_response(tmpl, output,
context_instance=RequestContext(request))
return wrapper
return renderer
Конечно, можно дописать пару строк к возврату враппера и получить в принципе работающий механизм:
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
return wrapper
Но на самом деле это не самый красивый метод. Куча всяких руководств для начинающих
по написанию декораторов всегда рекомендует юзать модуль Мишеля Симионато -
decorator. В принципе, всё конечно просто отлично, но есть у него огромный
недостаток - это дополнительная библиотека, в то время как есть отличное решение
из стандартной библиотеки - functools.wraps. Его использование ничем не
отличается от использования decorator‘а:
from functools import wraps
def render_to(tmpl):
def renderer(func):
@wraps(func)
def wrapper(request, *args, **kw):
output = func(request, *args, **kw)
if not isinstance(output, dict):
return output
return render_to_response(tmpl, output,
context_instance=RequestContext(request))
return wrapper
return renderer
И всё становится белым и пушистым. :-) Один момент непонятен - почему его не
пишут во всех этих руководствах для начинающих? Ведь эти начинающие с течением времени становятся продолжающими и точно так же не знают о простой и приятной штуке прямо в stdlib‘е.

Comments: 9 Subscribe (already: 0) Comment post
Молодец, что просвещаешь общественность. Уважаю.
Надо добавить, что это появилось только Python 2.5, поэтому не всем эти радости доступны.
Хм. Действительно. Просто у меня сейчас везде вокруг 2.5 (почти тепличные условия, хе-хе :), потому такие моменты иногда проходят мимо внимания. :-)
Господа, а можете ли вы пожалуйста либо рассказать, либо дать ссылку на что-то понятное про эти самые декораторы? Что-то никак не могу до конца ухватить суть… Все где-то вокруг да около… Тот же render_to - столько вложений def’ов я так понял нужно, а зачем? Потом вызов декоратора чем-то отличается от простой функции или нет? На сайте Джанги (http://code.djangoproject.com/wiki/CookBookShortcutsPageDecorator) вызов декоратора, аналога render_to, такой потом хитрый… Я так понял, что если нужно задекорировать сущестующую функцию, то пишем так:
render_to_responce = renderer(render_to_responce)и тогда при вызове render_to_responce вызовется декоратор renderer…если нужно применить декоратор к новой функции, то что-то вроде этого:
@render_to('cool.html') def show_cool_template(request): ...ух…
Попробую объяснить без теоретизирования (мне самому было в теорию сложно въехать, с практикой лучше пошло), на примере
render_to.Смотри.
render_toпринимает параметр - имя темплейта, возвращает функцию-обёртку (собственно сам декоратор) -renderer.rendererпринимает как параметр функцию (т.е. илиmy_view = renderer(my_view), или та самая конструкция с@), возвращает ещё одну функцию -wrapper, которая собственно… подменяет собой оригинальную.Этот
wrapperпринимает все параметры, которые может принять оригинальная функция, вызывает её, а потом проверяет на то, словарь ли это - если не словарь, то возвращает как есть, если словарь - то возвращает с помощью render_to_response.Если бы не было той самой первой функции, которая вызывается с именем темплейта, было бы на 1
defменьше.На тему синтаксиса:
это просто сахар для
Т.е. в питоне 2.3 приходилось пользоваться только второй конструкцией. Помимо того, что она и так не сильно приятная, тот же
render_toвыглядел бы так:Напряжно для понимания. :-)
Надеюсь, всё понятно? :-)
вроде все так красиво и понятно написано, но у меня почему-то вываливается ошибка: The view django.contrib.auth.decorators._CheckLogin.call didn’t return an HttpResponse object. может подскажешь из-за чего?
Вот просто так сходу - не подскажу… Надо на код смотреть.
Точнее, это характерная довольно ошибка, но я просто не помню, в чём дело. Можешь ещё попробовать на форуме спросить.
Александр, сорри за такой спам коментов - я действительно несколько раз пробовал разместить комент (даже в Trac ошибку постил) - но он так ни разу и не разместился, кроме как позавчера. О чем мне и было сказано в письме с просьбой его заапрувить. Что я смог сделать только что… и увидел вот такое…
Да ничего, мне не сложно их поудалять было. Разве что в RSS спама немного пролетело. :-)
Непонятно, почему он сейчас автоматом не заапрувился. Я посмотрю.
Comment form for «Пара слов о декораторах»