Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Decorators' recipes

Yury Yurevich's topic "Decorators' recipes" from PyCamp Kyiv 2010 (also known as PyKyiv)

  • Be the first to comment

Decorators' recipes

  1. 1. Рецепты декораторов Юрий Юревич yury.yurevich@equelli.com | http://pyobject.ru PyCamp Kyiv, Киев, 2010. 1
  2. 2. О докладчике ● Живу от Киева >2000 км ● В области разработки ПО >7 лет ● С Python >5 лет ● Блог о Python >3 лет ● Конференция по Ruby&Python >2 лет 2
  3. 3. О докладе ● Ингредиенты ● Функция — объект первого рода ● Декоратор — обёртка ● Синтаксический сахар ● Рецепты ● Инфраструктура ● Интерфейс ● Адаптер ● Гард ● Примеры из реальной жизни 3
  4. 4. Ингредиенты 4
  5. 5. Ингредиенты (2) Декоратор = функции(объекты первого рода) + вложенные функции + @синтаксический_сахар 5
  6. 6. Функция — объект первого рода >>> def give_me_twice(x): ... return x*2 ... >>> function <function give_me_twice at 0xb76278b4> >>> def call_it(func, arg) ... return func(arg) ... >>> def make_strange(func) ... replaced = lambda x: func(x + 5) ... return replaced ... >>> strange_twice = make_strange(give_me_twice) 6
  7. 7. Декоратор — обёртка функции >>> def give_me_twice(x): ... return x*2 ... >>> def make_strange(func) ... replaced = lambda x: func(x + 5) ... return replaced ... >>> give_me_twice = make_strange(give_me_twice) декоратор 7
  8. 8. Вложенные функции >>> def give_me_twice(x): ... return x*2 ... >>> def make_really_strange(func): ... def wrapper(z): ... res = func(z + 2) ... return res ... return wrapper ... >>> give_me_twice = make_really_strange(give_me_twice) >>> give_me_twice <function wrapper at 0xb7570924> 8
  9. 9. Фабрика! >>> def give_me_twice(x): ... return x*2 ... >>> def make_really_omg(num): ... def decor(func): ... def wrapper(z): ... res = func(z + num) ... return res ... return wrapper ... return decor ... >>> decorator = make_really_omg(5) >>> decorator <function decor at 0xb7570336> >>> give_me_twice = decorator(give_me_twice) >>> give_me_twice <function wrapper at 0xb7570823> 9
  10. 10. Еще и классы >>> def make_really_strange(func): func → wrapper ... def wrapper(z): ... res = func(z + 2) ... return res ... return wrapper >>> class make_really_strange(object): func → <instance> ... def __init__(self, func): ... self.func = func ... def __call__(self, z): ... res = self.func(z + 2) ... return res 10
  11. 11. Синтаксический сахар @name — неявный вызов name(func) «Обычный» >>> def give_me_twice(x): ... return x*2 ... >>> give_me_twice = make_really_omg(5)(give_me_twice) «Подслащенный» >>> @make_really_omg(5) ... def give_me_twice(x): ... return x*2 ... 11
  12. 12. Дайте два! ● «снизу вверх» ● «изнутри наружу» ● Не очень красиво @usecase @render_to('usecase_research.html') @usecase_provider @rules.apply_(default_rules) def research_unit(request): ... usecase(render_to('usecase_research.html') (usecase_provider(rules.apply_(default_rules) (research_unit)))) 12
  13. 13. Рецепты 13
  14. 14. Декоратор — «вынесение общего знаменателя за скобки» По сути, пересмотр паттернов ООП для Python и реализация средствами декораторов. 14
  15. 15. Инфраструктура 15
  16. 16. Инфраструктура ● Изменяет окружение ● Не меняет функцию/аргументы (обычно) ● Примеры: ● @commit ● @cache ● Псевдокод >>> def infrastructure(func): ... def wrapper(*args, **kwargs): ... prepare_environ() ... res = func(*args, **kwargs) ... fix_environ() ... return res ... return wrapper 16
  17. 17. Интерфейс 17
  18. 18. Интерфейс ● Регистрация однородных объектов ● Проверка требований (опционально) ● Примеры: ● @register.tag (Django) ● Псевдокод >>> CALLBACKS = [] >>> def callback(func): ... def wrapper(*args, **kwargs): ... res = func(*args, **kwargs) ... assert res is not None ... return res ... CALLBACKS.append(wrapper) ... return wrapper 18
  19. 19. Адаптер 19
  20. 20. Адаптер ● Согласование API, преобразование данных ● Часто — фабрики ● Примеры: ● @render_to (Django, Александр Соловьев) ● @permalink (Django) ● Псевдокод >>> def render_to(template): ... def decor(view): ... def wrapper(request, *args, **kwargs): ... context = view(request, *args, **kwargs) ... return render(template, request, context) ... return wrapper ... return decor 20
  21. 21. Гард 21
  22. 22. Гард ● Вынос типичного условного перехода ● Примеры: ● @login_required (Django) ● @validate (Pylons) ● Псевдокод >>> def login_required(view): ... def wrapper(request, *args, **kwargs): ... if request.user.is_authenticated(): ... return view(request, *args, **kwargs) ... else: ... return HttpResponseForbidden() ... return wrapper 22
  23. 23. Реальная жизнь - ajax_request def ajax_request(func): def wrapper(request, *args, **kwargs): гард if request.method == 'POST': response = func(request, *args, **kwargs) else: response = { 'error': { 'type': 405, 'message': 'Accepts POST only' } } адаптер if isinstance(response, dict): resp = JsonResponse(response) if 'error' in response: resp.status_code = response['error'].get('type', 500) return resp return response return functools.wraps(func)(wrapper) http://is.gd/7h6H7 23
  24. 24. Реальная жизнь - reg.simple_tag def simple_tag(self, func): params, xx, xxx, defaults = getargspec(func) class SimpleNode(Node): [...] адаптер compile_func = curry( generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode ) compile_func.__doc__ = func.__doc__ интерфейс self.tag( getattr(func, "_decorated_function", func).__name__, compile_func ) return func http://is.gd/7hdMu 24
  25. 25. Кунг-фу Это к Сергею Щетинину ;) http://self.maluke.com/ 25
  26. 26. P.S. Ссылки ● http://www.siafoo.net/article/68 ● http://pypi.python.org/pypi/decorator ● http://tinyurl.com/decorator-guard 26
  27. 27. Спасибо за внимание Вопросы? 27

×