Mais conteúdo relacionado Semelhante a Decorators' recipes (20) Mais de Yury Yurevich (12) Decorators' recipes2. О докладчике
● Живу от Киева >2000 км
● В области разработки ПО >7 лет
● С Python >5 лет
● Блог о Python >3 лет
● Конференция по Ruby&Python >2 лет
2
3. О докладе
● Ингредиенты
● Функция — объект первого рода
● Декоратор — обёртка
● Синтаксический сахар
● Рецепты
● Инфраструктура
● Интерфейс
● Адаптер
● Гард
● Примеры из реальной жизни
3
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. Декоратор — обёртка функции
>>> 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. Вложенные функции
>>> 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. Фабрика!
>>> 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. Еще и классы
>>> 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. Синтаксический сахар
@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. Дайте два!
● «снизу вверх»
● «изнутри наружу»
● Не очень красиво
@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
14. Декоратор —
«вынесение общего
знаменателя за скобки»
По сути, пересмотр паттернов ООП для Python
и реализация средствами декораторов.
14
16. Инфраструктура
● Изменяет окружение
● Не меняет функцию/аргументы (обычно)
● Примеры:
● @commit
● @cache
● Псевдокод
>>> def infrastructure(func):
... def wrapper(*args, **kwargs):
... prepare_environ()
... res = func(*args, **kwargs)
... fix_environ()
... return res
... return wrapper 16
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
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
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. Реальная жизнь - 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. Реальная жизнь - 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. Кунг-фу
Это к Сергею Щетинину ;)
http://self.maluke.com/
25
26. P.S. Ссылки
● http://www.siafoo.net/article/68
● http://pypi.python.org/pypi/decorator
● http://tinyurl.com/decorator-guard
26