Декораторы
Содержание
План рассказа
Поправка к предыдущему рассказу: слайсы лучше делать через _*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]
