Декораторы
Содержание
План рассказа
Поправка к предыдущему рассказу: слайсы лучше делать через _*item__, пример. (slice.start, slice.stop, slice.step, slice.indices(len))
- Основная идея: мы хотим довешивать к функциям новые заголовки, которых нет в языке; как можно задавать семантику таким заголовкам?
- Пример: memoizing
- Формальное определение
Про метод __call__
- Пример: logging
- Декораторы с параметрами. Пример: acl
- Напоследок: стопки декораторов.
Полезные ссылки
Введение, близкое к моему порядку изложения, но без примеров (англ)
Официальное описание языка на тему определения функций (англ)
Charming Python: Decorators make magic easy -- примеры весьма навороченной магии с помощью декораторов (англ) (я бы назвал этот текст: Black magic with decorators. Безо всяких там слов easy. Довольно неплохой способ хорошенько растянуть извилины).
Встроенные декораторы в питоне
- staticmethod
- про это расскажет Саша на следующем занятии
- classmethod
- про это расскажет Саша на следующем занятии
- property
- про это расскажет Саша на следующем занятии
- contextlib.contextmanager
- как просто делать свои обработчики входа и выхода для with
- functools.wraps
- вспомогательная обёртка для создания декораторов (копирует самодокументацию и пр.)
Идеи для декораторов
- logging
- записывать вызов и, например, аргументы функции в лог
- profiling
- записывать в лог длительность исполнения функции или суммарную длительность всех исполнений или суммарную длительность исполнений с одинаковыми аргументами
- memoizing
- запоминать результаты функции для заданного набора аргументов, чтобы не пересчитывать заново
- once / lazyproperty
- первый раз вызывать вычисление функции, последующие разы возвращать первый результат (фактически, синоним memoizing для функции без аргументов)
- alias
- создавать функцию сразу с несколькими именами
- curry
- создавать функцию, которая умеет выполнять операцию частичного вызова
- typecheck
- проверять типы аргументов функции (в момент вызова)
- multimethod
- выбирать, какое тело функции исполнять, в зависимости от типов аргументов
- transaction
- следить, чтобы все части действия либо выполнились вместе, либо из них не выполнилась ни одна; записывать информацию о совершённом действии так, чтобы его было легко отменить (это очень специфично в зависимости от конкретной области применения)
- check_access
- проверять права доступа перед исполнением функции
Генератор генераторов декораторов из (генераторов) итераторов
1 import functools
2 class Interface(object):
3 def __init__(self, **kwargs):
4 vars(self).update(kwargs)
5 def gendecorator(geniterator):
6 def make_decorator(*gen_args, **gen_kw):
7 def decorator(fun):
8 @functools.wraps(fun)
9 def newfun(*args, **kw):
10 interface = Interface(args=args, kw_args=kw, fun=fun)
11 iterator = geniterator(interface, *gen_args, **gen_kw)
12 for _ in iterator:
13 assert not hasattr(interface, 'result'), (
14 "Generator '%s' must have exactly one yield" %
15 geniterator.__name__
16 )
17 interface.result = (
18 fun(*interface.args, **interface.kw_args)
19 )
20 return interface.result
21 return newfun
22 return decorator
23 return make_decorator
24
25 # Example use
26
27 @gendecorator
28 def returns(interface, type_):
29 yield
30 assert type(interface.result) == type_
31
32 @gendecorator
33 def accepts(interface, *types, **kw_types):
34 for arg, type_ in zip(interface.args, types):
35 assert type(arg) == type_
36 for name in kw_types:
37 assert type(interface.kw_args[name]) == kw_types[name]
38 yield
39
40 import time
41 @gendecorator
42 def profiling(interface):
43 begin = time.time()
44 yield
45 end = time.time()
46 print "Time spent in %s: %0.5f" % (interface.fun.__name__, end - begin)
47
48 @gendecorator
49 def logging(interface):
50 print "Entering %s" % interface.fun.__name__
51 yield
52 print "Leaving %s" % interface.fun.__name__
53
54 @logging()
55 @profiling()
56 @accepts(int, int)
57 @returns(int)
58 def sum(a, b):
59 return a + b
Контрольная работа
Напишите итератор reversed(list), который получает на вход список и обходит его в обратном порядке.
Пользуясь этим итератором, напишите программу, которая выводит на экран квадраты последних 15 чисел из диапазона [0, 9000]