decorators' recipes

27
1 Рецепты декораторов Юрий Юревич [email protected] | http://pyobject.ru PyCamp Kyiv, Киев, 2010.

Upload: yury-yurevich

Post on 16-Jun-2015

1.394 views

Category:

Technology


4 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Decorators' recipes

1

Рецепты декораторов

Юрий Юревич [email protected] | http://pyobject.ru

PyCamp Kyiv, Киев, 2010.

Page 2: Decorators' recipes

2

О докладчике

● Живу от Киева >2000 км● В области разработки ПО >7 лет● С Python >5 лет● Блог о Python >3 лет● Конференция по Ruby&Python >2 лет

Page 3: Decorators' recipes

3

О докладе

● Ингредиенты● Функция — объект первого рода● Декоратор — обёртка● Синтаксический сахар

● Рецепты● Инфраструктура● Интерфейс● Адаптер● Гард

● Примеры из реальной жизни

Page 4: Decorators' recipes

4

Ингредиенты

Page 5: Decorators' recipes

5

Ингредиенты (2)

Декоратор = функции(объекты первого рода) + вложенные функции + @синтаксический_сахар

Page 6: Decorators' recipes

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)

Page 7: Decorators' recipes

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)

декоратор

Page 8: Decorators' recipes

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>

Page 9: Decorators' recipes

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>

Page 10: Decorators' recipes

10

Еще и классы

>>> def make_really_strange(func):... def wrapper(z):... res = func(z + 2)... return res... return wrapper

>>> class make_really_strange(object):... def __init__(self, func):... self.func = func... def __call__(self, z):... res = self.func(z + 2)... return res

func → wrapper

func → <instance>

Page 11: Decorators' recipes

11

Синтаксический сахар

>>> def give_me_twice(x):... return x*2...

>>> give_me_twice = make_really_omg(5)(give_me_twice)

@name — неявный вызов name(func)

«Подслащенный»

«Обычный»

>>> @make_really_omg(5)... def give_me_twice(x):... return x*2...

Page 12: Decorators' recipes

12

Дайте два!

● «снизу вверх»● «изнутри наружу»● Не очень красиво@usecase@render_to('usecase_research.html')@[email protected]_(default_rules)def research_unit(request): ...

usecase(render_to('usecase_research.html')(usecase_provider(rules.apply_(default_rules)(research_unit))))

Page 13: Decorators' recipes

13

Рецепты

Page 14: Decorators' recipes

14

Декоратор — «вынесение общего

знаменателя за скобки»

По сути, пересмотр паттернов ООП для Pythonи реализация средствами декораторов.

Page 15: Decorators' recipes

15

Инфраструктура

Page 16: Decorators' recipes

16

Инфраструктура

● Изменяет окружение● Не меняет функцию/аргументы (обычно)● Примеры:

● @commit● @cache

● Псевдокод>>> def infrastructure(func):... def wrapper(*args, **kwargs):... prepare_environ()... res = func(*args, **kwargs)... fix_environ()... return res... return wrapper

Page 17: Decorators' recipes

17

Интерфейс

Page 18: Decorators' recipes

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

Page 19: Decorators' recipes

19

Адаптер

Page 20: Decorators' recipes

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

Page 21: Decorators' recipes

21

Гард

Page 22: Decorators' recipes

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

Page 23: Decorators' recipes

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

Page 24: Decorators' recipes

24

Реальная жизнь - reg.simple_tagdef 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

Page 25: Decorators' recipes

25

Кунг-фу

Это к Сергею Щетинину ;)

http://self.maluke.com/

Page 26: Decorators' recipes

26

P.S. Ссылки

● http://www.siafoo.net/article/68 ● http://pypi.python.org/pypi/decorator● http://tinyurl.com/decorator-guard

Page 27: Decorators' recipes

27

Спасибо за внимание

Вопросы?