четверг, 9 июля 2009 г.

Python декораторы

Казалось бы всё просто: декораторы – это просто статические фукции.

Но ведь это функции, а в функции вполне можно передавать различные параметры… Причём не обязательно это должны быть константные значение либо имена типов (как пользуются декораторами почти везде). В декоратор можно передать значения считаные из файла настроек; главное, чтобы значение было определено до того как объявляется применение самого декоратора к функции. Для примера декоратор @measures получает на вход 3 разных значения, а значит функции linerFib, linerFib1 и stubFib будут “обрамляться” разными строками.

  1: teststring = 'test1'
 2: teststring1 = 'test2'
 3:  
 4: ...
 5:  
 6: @measures(teststring)
 7: def linerFib( n ):
 8:     fn = f1 = f2 = 1
 9:     for counter in xrange(2, n):
 10:        fn = f1 + f2
 11:         f2, f1 = f1, fn
 12: return fn
 13:  
 14: @measures(teststring1)
 15: def linerFib1( n ):
 16:     fn = f1 = f2 = 1
 17:     for counter in xrange(2, n):
 18:         fn = f1 + f2
 19:         f2, f1 = f1, fn
 20: return fn
 21:  
 22: teststring1 = 'test xxx'
 23: @measures(teststring1)
 24: def stubFib( n ):
 25:     return 5

Но если бы всё только этим и ограничивалось… Самое интресное наступает, когда понимаешь, что декораторы могут возвращать и лямбда-функции. А ещё интересней становится, когда доходит что через лямбда-функцию можно вернуть не результат самой декорируемой функции, а экземпляр объекта, в котором есть метод __call__() , который и будет возвращать результат декорируемой функции. Да - это кажется перебором с вложеностью матрёшек, но вот с логической точки зрения провести разделение подготовки к вызову декорируемой функции и пост-обратотку её результата кажется довольно аппетитным… Т.е. можно провернуть следующее:

 1: class Drawer(object):
 2: def __init__(self, func, test):
# подготовка и инициализация для вызова декорируемой функции
 3:     self.func = func
 4:     self.test = test
 5:  
 6: def __call__(self, *args):
# сам вызов
 7:     print self.test
 8:     return self.func( *args )
 9:  
 10: def measures(test):
 11:     return lambda func: Drawer(func, test)

Да, в большинстве случаев это будет довольно избыточно, но когда хочешь выводить график декорируемой функции в pygame, при этом не смешивая вывод результата на экран и расчёты и обработку результатов функции, то такие матрёшки становятся вполне удобными.

Комментариев нет:

Отправить комментарий