Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Декораторы  Python
Про доклад <ul><li>Материала много </li></ul><ul><li>Возможно слишком много </li></ul><ul><li>И не только про декораторы <...
Забегая вперед <ul><li>@threaded   # <- декоратор def func(x):   ... </li></ul><ul><li>@event(‘close’)   # <- тоже декорат...
Функция как объект и объект как функция <ul><li>>>>  def f(x): return str(x) </li></ul><ul><li>... </li></ul><ul><li>>>>  ...
Синтаксис и семантика декоратора <ul><li>Вместо def func():  </li></ul><ul><li>… func = wrap(func) </li></ul><ul><li>мы мо...
Зачем нужны декораторы? <ul><li>Декораторы позволяют сделать код более читабельным </li></ul><ul><li>Требования к каждой о...
Что может делать декоратор? <ul><li>Декоратор &quot;оборачивает&quot; функцию </li></ul><ul><ul><li>может исполнять код до...
Например <ul><li>def transact( method ): </li></ul><ul><li>def transacted_call( self, * args, **kw ): </li></ul><ul><li>tr...
Что за звёздочки? <ul><li>def transacted_call( self,  * args,  ** kw):   </li></ul><ul><li>r = method( self,  * args,  ** ...
Как это будет работать? <ul><li>def transact(method): </li></ul><ul><li>def transacted_call( self, * ): </li></ul><ul><li>...
И где же читабельность? <ul><li>class C: </li></ul><ul><li>@transact </li></ul><ul><li>def update(self, what): </li></ul><...
Еще один пример <ul><li>Сценарий: </li></ul><ul><ul><li>Есть класс исходный код которого мы не можем менять непосредственн...
Инъекция методов <ul><li>class A: </li></ul><ul><li>def m1(self): pass </li></ul><ul><li>def m2(self): pass </li></ul><ul>...
Как это было сделано <ul><li>То что идет после  @  вычисляется в момент импорта модуля </li></ul><ul><li>Вычисленное должн...
Собственно код <ul><li>def inject_into(cls): </li></ul><ul><li>def do_inject(method): </li></ul><ul><li>name = method.__na...
И всё таки как же это работает? <ul><li>В выражении  do_inject( m2)   нигде не упоминается класс в который мы делаем инъек...
Как об этом удобней думать <ul><li>Можно смотреть на определение функций как на исполнение последовательности команд интер...
Как это можно было сделать иначе <ul><li>class Injector: </li></ul><ul><li>def __init__(self, cls): </li></ul><ul><li>self...
Примеры применения <ul><li>@threaded, @lock(obj) </li></ul><ul><li>@exposed, @xml_rpc, @json </li></ul><ul><li>@cache_resu...
Примеры применения в  веб-приложениях <ul><li>@template(template_name) </li></ul><ul><li>@default_values(key=value, ..) </...
Пример применения в настольных приложениях <ul><li>class FeedbackWin(Dialog): </li></ul><ul><li>send = Button(&quot;Send&q...
Совсем уж необычный пример <ul><li>@ a sync </li></ul><ul><li>def calculate(win): </li></ul><ul><li>win.status.set('Calcul...
Когда стоить подумать о декораторах <ul><li>Всегда :) </li></ul><ul><li>Когда набору функций надо придать какие-то общие с...
Спасибо за внимание <ul><li>Оффтоп: если у вас есть идеи, время или желание сделать что-то интересное, а лучше всё вместе,...
Upcoming SlideShare
Loading in …5
×

Декораторы в Python и их практическое использование

6,411 views

Published on

Доклад был дан на конференции Exception #03 в январе 2007.

Published in: Technology
  • хорошая и понятная презентация
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Доклад был дан на конференции Exception #03 в январе 2007.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Декораторы в Python и их практическое использование

  1. 1. Декораторы Python
  2. 2. Про доклад <ul><li>Материала много </li></ul><ul><li>Возможно слишком много </li></ul><ul><li>И не только про декораторы </li></ul><ul><li>Теория вводится вперемешку с практикой в надежде что так легче понять </li></ul><ul><li>Кода тоже много </li></ul><ul><li>Задавайте вопросы по слайдам пока не поздно </li></ul>
  3. 3. Забегая вперед <ul><li>@threaded # <- декоратор def func(x): ... </li></ul><ul><li>@event(‘close’) # <- тоже декоратор def on_close(self, evt): ... </li></ul><ul><li>@template(‘article’) # <- очередной декоратор @default_slots(title=‘Untitled’, author=‘anon’) # <- и снова декоратор def article(self, **kw): return {…} </li></ul>
  4. 4. Функция как объект и объект как функция <ul><li>>>> def f(x): return str(x) </li></ul><ul><li>... </li></ul><ul><li>>>> f </li></ul><ul><li><function f at 0x009ED5B0> </li></ul><ul><li>>>> isinstance(f, object) </li></ul><ul><li>True </li></ul><ul><li>>>> callable(f) </li></ul><ul><li>True </li></ul><ul><li>>>> f(123) </li></ul><ul><li>'123' </li></ul><ul><li>>>> class F(object): </li></ul><ul><li>... def __call__(self, x): </li></ul><ul><li>... return str(x) </li></ul><ul><li>... </li></ul><ul><li>>>> f = F() </li></ul><ul><li>>>> f </li></ul><ul><li><__main__.F object at 0x009E7B50> </li></ul><ul><li>>>> isinstance(f, object) </li></ul><ul><li>True </li></ul><ul><li>>>> callable(f) </li></ul><ul><li>True </li></ul><ul><li>>>> f(123) </li></ul><ul><li>'123' </li></ul><ul><li>>>> callable(object()) </li></ul><ul><li>False </li></ul>
  5. 5. Синтаксис и семантика декоратора <ul><li>Вместо def func(): </li></ul><ul><li>… func = wrap(func) </li></ul><ul><li>мы можем писать </li></ul><ul><li>@wrap def func(): </li></ul><ul><li>… </li></ul>
  6. 6. Зачем нужны декораторы? <ul><li>Декораторы позволяют сделать код более читабельным </li></ul><ul><li>Требования к каждой отдельной функции (или методу) упрощаются </li></ul><ul><ul><li>Самый сложный или скучный код можно отдалить от основного </li></ul></ul><ul><ul><li>Можно меньше повторяться </li></ul></ul><ul><li>Это еще один способ разбиения кода </li></ul><ul><li>Благодаря декораторам мы можем сделать многие абстракции более практичными </li></ul>
  7. 7. Что может делать декоратор? <ul><li>Декоратор &quot;оборачивает&quot; функцию </li></ul><ul><ul><li>может исполнять код до вызова </li></ul></ul><ul><ul><ul><li>изменять параметры </li></ul></ul></ul><ul><ul><ul><li>проверять типы параметров </li></ul></ul></ul><ul><ul><ul><li>печатать отладочную информацию </li></ul></ul></ul><ul><ul><li>может исполнять код после вызова </li></ul></ul><ul><ul><ul><li>проверять результат </li></ul></ul></ul><ul><ul><ul><li>преобразовывать его </li></ul></ul></ul><ul><ul><li>повторять вызов в каких-то случаях </li></ul></ul><ul><ul><ul><li>ошибка сети? – попробуй еще пару раз </li></ul></ul></ul><ul><ul><li>перемежать выполнение своей логикой (генераторы) </li></ul></ul><ul><ul><li>напрашивающийся пример: транзакции </li></ul></ul>
  8. 8. Например <ul><li>def transact( method ): </li></ul><ul><li>def transacted_call( self, * args, **kw ): </li></ul><ul><li>try: </li></ul><ul><li>transaction = self.start_transaction() </li></ul><ul><li>r = method( self, *args, **kw) </li></ul><ul><li>transaction.commit() </li></ul><ul><li>return r </li></ul><ul><li>except: </li></ul><ul><li>transaction.rollback() </li></ul><ul><li>raise </li></ul><ul><li>return transacted_call </li></ul><ul><li>class C: </li></ul><ul><li>@transact </li></ul><ul><li>def update(self, ...): </li></ul><ul><li>... </li></ul>
  9. 9. Что за звёздочки? <ul><li>def transacted_call( self, * args, ** kw): </li></ul><ul><li>r = method( self, * args, ** kw) </li></ul><ul><li>>>> def lets_see(*args, **kw): </li></ul><ul><li>... print 'args =', args, 'kw =', kw </li></ul><ul><li>>>> lets_see(1, 2, 3) </li></ul><ul><li>args = (1, 2, 3) kw = {} </li></ul><ul><li>>>> lets_see(a=1, b=2) </li></ul><ul><li>args = () kw = {'a': 1, 'b': 2} </li></ul><ul><li>>>> lets_see(1, b=2) </li></ul><ul><li>args = (1,) kw = {'b': 2} </li></ul>
  10. 10. Как это будет работать? <ul><li>def transact(method): </li></ul><ul><li>def transacted_call( self, * ): </li></ul><ul><li>try: </li></ul><ul><li>transaction = self.sta.. </li></ul><ul><li>r = method( self, * ) </li></ul><ul><li>transaction.commit() </li></ul><ul><li>return r </li></ul><ul><li>except: </li></ul><ul><li>transaction.rollback() </li></ul><ul><li>raise </li></ul><ul><li>return transacted_call </li></ul><ul><li>class C: </li></ul><ul><li>@transact </li></ul><ul><li>def update(self, what): </li></ul><ul><li>bla-bla-bla </li></ul><ul><li>return X </li></ul><ul><li>class C: </li></ul><ul><li>def update(self, what): </li></ul><ul><li>try: </li></ul><ul><li>transaction = self.sta.. </li></ul><ul><li>bla-bla-bla </li></ul><ul><li>transaction.commit() </li></ul><ul><li>return X </li></ul><ul><li>except: </li></ul><ul><li>transaction.rollback() </li></ul><ul><li>raise </li></ul>
  11. 11. И где же читабельность? <ul><li>class C: </li></ul><ul><li>@transact </li></ul><ul><li>def update(self, what): </li></ul><ul><li>bla-bla-bla </li></ul><ul><li>return X </li></ul><ul><li>@transact </li></ul><ul><li>def insert(self, what): </li></ul><ul><li>foo-foo-foo </li></ul><ul><li>return Y </li></ul><ul><li>class C: </li></ul><ul><li>def update(self, what): </li></ul><ul><li>try: </li></ul><ul><li>transaction = self.sta.. </li></ul><ul><li>bla-bla-bla </li></ul><ul><li>transaction.commit() </li></ul><ul><li>return X </li></ul><ul><li>except: </li></ul><ul><li>transaction.rollback() </li></ul><ul><li>raise </li></ul><ul><li>def insert(self, what): </li></ul><ul><li>try: </li></ul><ul><li>transaction = self.sta.. </li></ul><ul><li>foo-foo-foo </li></ul><ul><li>transaction.commit() </li></ul><ul><li>return Y </li></ul><ul><li>except: </li></ul><ul><li>transaction.rollback() </li></ul><ul><li>raise </li></ul>
  12. 12. Еще один пример <ul><li>Сценарий: </li></ul><ul><ul><li>Есть класс исходный код которого мы не можем менять непосредственно потому что: </li></ul></ul><ul><ul><ul><li>у нас нет его исходного кода </li></ul></ul></ul><ul><ul><ul><li>он из чужой библиотеки и будет меняться </li></ul></ul></ul><ul><ul><li>У этого класса есть множество наследников </li></ul></ul><ul><ul><li>Мы хотим расширить этот класс (добавить или переопределить методы) и хотим чтобы изменения были унаследованы </li></ul></ul>
  13. 13. Инъекция методов <ul><li>class A: </li></ul><ul><li>def m1(self): pass </li></ul><ul><li>def m2(self): pass </li></ul><ul><li>class B(A): pass </li></ul><ul><li>def m1_ext(self): </li></ul><ul><li>#A.m1(self) !error </li></ul><ul><li>return True </li></ul><ul><li>A.m1 = m1_ext </li></ul><ul><li>assert B().m1() </li></ul><ul><li>@inject_into(A) </li></ul><ul><li>def m2(self): </li></ul><ul><li>self._A_m2() </li></ul><ul><li>return True </li></ul><ul><li>assert B().m2() </li></ul>
  14. 14. Как это было сделано <ul><li>То что идет после @ вычисляется в момент импорта модуля </li></ul><ul><li>Вычисленное должно быть функцией </li></ul><ul><li>Эта функция должна принимать один параметр – функцию </li></ul><ul><li>Возвращать также надо функцию </li></ul><ul><li>То есть @inject_into(A) означает что вызов inject_into(A) происходит только однажды и возвращает собственно декоратор </li></ul>
  15. 15. Собственно код <ul><li>def inject_into(cls): </li></ul><ul><li>def do_inject(method): </li></ul><ul><li>name = method.__name__ </li></ul><ul><li>saved_name = '_%s_%s' % (cls.__name__, name) </li></ul><ul><li>setattr(cls, saved_name, getattr(cls, name)) </li></ul><ul><li>setattr(cls, name, method) </li></ul><ul><li>return do_inject </li></ul><ul><li>в нашем примере сначала исполнялся inject_into( A ) </li></ul><ul><li>мы получали в результате do_inject </li></ul><ul><li>в определенном смысле на дальнейшее можно смотреть вот так: </li></ul><ul><li>@ do_inject </li></ul><ul><li>def m2(self): </li></ul><ul><li>что в свою очередь эквивалентно </li></ul><ul><li>do_inject( m2) </li></ul>
  16. 16. И всё таки как же это работает? <ul><li>В выражении do_inject( m2) нигде не упоминается класс в который мы делаем инъекцию. </li></ul><ul><li>Это возможно за счет того что функция do_inject определена внутри другой функции и использует параметр этой внешней функции который в нашем примере cls=A </li></ul><ul><li>Напоминаю код: </li></ul><ul><ul><li>def inject_into( cls ): </li></ul></ul><ul><ul><li>def do_inject(method): </li></ul></ul><ul><ul><li>name = method.__name__ </li></ul></ul><ul><ul><li>saved_name = '_%s_%s' % ( cls.__name__ , name) </li></ul></ul><ul><ul><li>setattr( cls , saved_name, getattr( cls , name)) </li></ul></ul><ul><ul><li>setattr( cls , name, method) </li></ul></ul><ul><ul><li>return do_inject </li></ul></ul>
  17. 17. Как об этом удобней думать <ul><li>Можно смотреть на определение функций как на исполнение последовательности команд интерпретатору ( так оно и есть) </li></ul><ul><li>таким образом сколько раз мы вызываем inject_into столько разных do_inject мы получаем в итоге, ведь код определения этой внутренней функции исполняется каждый раз заново. </li></ul>
  18. 18. Как это можно было сделать иначе <ul><li>class Injector: </li></ul><ul><li>def __init__(self, cls): </li></ul><ul><li>self.target_cls = cls </li></ul><ul><li>def __call__(self, method): </li></ul><ul><li>name = method.__name__ </li></ul><ul><li>cls = self.target_cls </li></ul><ul><li>saved_name = '_%s_%s' % (cls.__name__, name) </li></ul><ul><li>setattr(cls, saved_name, getattr(cls, name)) </li></ul><ul><li>setattr(cls, name, method) </li></ul><ul><li>@Injector(A) # тут декоратором оказывается экземпляр Injector </li></ul><ul><li>def m2(self): </li></ul><ul><li>... </li></ul>
  19. 19. Примеры применения <ul><li>@threaded, @lock(obj) </li></ul><ul><li>@exposed, @xml_rpc, @json </li></ul><ul><li>@cache_results, @memoize </li></ul><ul><li>@property </li></ul><ul><li>@staticmethod, @classmethod </li></ul><ul><li>@log_calls, @linetrace </li></ul><ul><li>@profile </li></ul><ul><li>@accepts, @returns </li></ul><ul><li>@meta(author=..) </li></ul>
  20. 20. Примеры применения в веб-приложениях <ul><li>@template(template_name) </li></ul><ul><li>@default_values(key=value, ..) </li></ul><ul><li>@require(group='admin') </li></ul><ul><li>@mimetype('text/plain') </li></ul><ul><li>@compact_spaces </li></ul><ul><li>@email_failures </li></ul>
  21. 21. Пример применения в настольных приложениях <ul><li>class FeedbackWin(Dialog): </li></ul><ul><li>send = Button(&quot;Send&quot;) </li></ul><ul><li>cancel = Button(&quot;Cancel&quot;) </li></ul><ul><li>@event('button', 'send', send=True) </li></ul><ul><li>@event('button', 'cancel', send=False) </li></ul><ul><li>def on_ done (self, evt, send): </li></ul><ul><li>if send: </li></ul><ul><li>... </li></ul><ul><li>self.close() </li></ul>
  22. 22. Совсем уж необычный пример <ul><li>@ a sync </li></ul><ul><li>def calculate(win): </li></ul><ul><li>win.status.set('Calculating...') </li></ul><ul><li>yield A SYNC </li></ul><ul><li>## ... страшные вычисления ... </li></ul><ul><li>yield SYNC </li></ul><ul><li>win.status.set('Writing results...') </li></ul><ul><li>yield A SYNC </li></ul><ul><li>## ... долго пишем много результатов ... </li></ul><ul><li>yield SYNC </li></ul><ul><li>win.status.set('Done') </li></ul><ul><li>Нечто отдаленно схожее есть в twisted , называется twisted flow </li></ul>
  23. 23. Когда стоить подумать о декораторах <ul><li>Всегда :) </li></ul><ul><li>Когда набору функций надо придать какие-то общие свойства </li></ul><ul><li>Когда вы замечаете что код выполняющий какую-то работу соседствует с кодом который занимается связкой (события, инъекции и т.п.) </li></ul><ul><li>Когда хочется разделить что-то на &quot;орех&quot; и &quot;скорлупу&quot; </li></ul>
  24. 24. Спасибо за внимание <ul><li>Оффтоп: если у вас есть идеи, время или желание сделать что-то интересное, а лучше всё вместе, подойдите после доклада. </li></ul>

×