Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Decorators: A Powerful Weapon in Your Python Ar...
Search
PyCon 2014
April 11, 2014
1.7k
10
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Decorators: A Powerful Weapon in Your Python Arsenal by Colton Myers
PyCon 2014
April 11, 2014
More Decks by PyCon 2014
See All by PyCon 2014
Postgres Performance for Humans by Craig Kerstiens
pycon2014
28
3.7k
Technical Onboarding, Training, and Mentoring by Kate Heddleston and Nicole Zuckerman
pycon2014
1
2.4k
"My big gay adventure. Making, releasing and selling an indie game made in python." by Luke Miller
pycon2014
2
1.7k
Farewell and Welcome Home, Python in Two Genders by Naomi_Ceder
pycon2014
1
820
Deliver Your Software in an Envelope by Augie Fackler and Nathaniel Manista
pycon2014
1
620
Hitchhikers Guide to Free and Open Source Participation by Elena Williams
pycon2014
6
1.3k
Localization Revisted (aka. Translations Evolved) by Ruchi Varshney
pycon2014
0
740
Smart Dumpster by Bradley E. Angell
pycon2014
0
590
Software Engineering for Hackers: Bridging the Two Solitudes by Tavish Armstrong
pycon2014
0
790
Featured
See All Featured
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
450
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
530
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
GitHub's CSS Performance
jonrohan
1033
470k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
290
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
58k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.6k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
520
Game over? The fight for quality and originality in the time of robots
wayneb77
1
290
Transcript
Decorators A Powerful Weapon in your Python Arsenal Colton Myers
@basepi ! http://bit.ly/dec-pycon-2014
None
Come visit our booth!
Open space tonight Room 523A 17:00-19:00
Salt Sprint on Monday
The Plan • What is a decorator? • How are
decorators constructed? • How do they work? • The “right” way to make decorators • Examples
What is a decorator?
@my_decorator def my_awesome_function(): pass
Decorators wrap functions
Decorators wrap functions • Add functionality • Modify behavior •
Perform setup/teardown • Diagnostics (timing, etc)
But first…
What is a function?
Everything in Python is an object.
Functions are objects
def myfunc(): print(‘hello!’)
myfunc() #prints “hello!” f = myfunc f() #prints “hello!” def
myfunc(): print(‘hello!’)
Functions can create other functions
def make_printer(word): def inner(): print(word) return inner ! ! p
= make_printer(‘such wow!’) p() #prints “such wow!”
None
This is called a closure
def make_printer(word): def inner(): print(word) return inner
def make_printer(word): def inner(): print(word) return inner
Decorators are closures
Decorators are closures …usually.
(Decorators can also be created with classes)
def make_printer(word): def inner(): print(word) return inner
def my_decorator(wrapped): def inner(*args, **kwargs): return wrapped(*args, **kwargs) return inner
def my_decorator(wrapped): def inner(*args, **kwargs): return wrapped(*args, **kwargs) return inner
@my_decorator def myfunc(): pass
def myfunc(): pass def my_decorator(wrapped): def inner(*args, **kwargs): return wrapped(*args,
**kwargs) return inner myfunc = my_decorator(myfunc)
def my_decorator(wrapped): def inner(*args, **kwargs): return wrapped(*args, **kwargs) return inner
@my_decorator def myfunc(): pass
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner @shout def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner @shout def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner @shout def myfunc(): print(‘such wow!’)
>>> myfunc() BEFORE! such wow! AFTER!
How does this work?
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner @shout def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
Good decorators are versatile
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
*args and **kwargs together take any number of positional and/or
keyword arguments
*args is a list **kwargs is a dictionary
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
*args becomes args[0], args[1], args[2], …
**kwargs becomes key0=kwargs[key0], key1=kwargs[key1], …
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
This decorator is still wrong.
>>> myfunc.__name__ ‘inner’
def shout(wrapped): def inner(*args, **kwargs): print(‘BEFORE!’) ret = wrapped(*args, **kwargs)
print(‘AFTER!’) return ret inner.__name__ = wrapped.__name__ return inner myfunc = shout(myfunc) def myfunc(): print(‘such wow!’)
>>> import inspect >>> inspect.getargspec(myfunc) ArgSpec(args=[], varargs='args', keywords='kwargs', defaults=None)
Graham Dumpleton created wrapt http://bit.ly/decorators2014
import wrapt ! @wrapt.decorator def pass_through(wrapped, instance, args, kwargs): return
wrapped(*args, **kwargs) ! @pass_through def function(): pass
import wrapt ! @wrapt.decorator def pass_through(wrapped, instance, args, kwargs): return
wrapped(*args, **kwargs) ! @pass_through def function(): pass
What about decorators with arguments?
@unittest.skipIf() (simplified)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec @skipIf(True, ‘I hate doge’) def myfunc(): print(‘very print’)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec myfunc = skipIf(True, ‘I hate doge’)(myfunc) def myfunc(): print(‘very print’)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec myfunc = skipIf(True, ‘I hate doge’)(myfunc) def myfunc(): print(‘very print’)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec myfunc = skipIf(True, ‘I hate doge’)(myfunc) def myfunc(): print(‘very print’)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec myfunc = skipIf(True, ‘I hate doge’)(myfunc) def myfunc(): print(‘very print’)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec myfunc = skipIf(True, ‘I hate doge’)(myfunc) def myfunc(): print(‘very print’)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec myfunc = skipIf(True, ‘I hate doge’)(myfunc) def myfunc(): print(‘very print’)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec myfunc = skipIf(True, ‘I hate doge’)(myfunc) def myfunc(): print(‘very print’)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec myfunc = skipIf(True, ‘I hate doge’)(myfunc) def myfunc(): print(‘very print’)
def skipIf(conditional, message): def dec(wrapped): def inner(*args, **kwargs): if not
conditional: return wrapped(*args, **kwargs) else #skipping print(message) return inner return dec @skipIf(True, ‘I hate doge’) def myfunc(): print(‘very print’)
What about with wrapt?
import wrapt ! def with_arguments(myarg1, myarg2): @wrapt.decorator def wrapper(wrapped, instance,
args, kwargs): return wrapped(*args, **kwargs) return wrapper ! @with_arguments(1, 2) def function(): pass
import wrapt ! def with_arguments(myarg1, myarg2): @wrapt.decorator def wrapper(wrapped, instance,
args, kwargs): return wrapped(*args, **kwargs) return wrapper ! @with_arguments(1, 2) def function(): pass
import wrapt ! def with_arguments(myarg1, myarg2): @wrapt.decorator def wrapper(wrapped, instance,
args, kwargs): return wrapped(*args, **kwargs) return wrapper ! @with_arguments(1, 2) def function(): pass
A few last examples
Counting function calls
def count(wrapped): def inner(*args, **kwargs): inner.counter += 1 return wrapped(*args,
**kwargs) inner.counter = 0 return inner @count def myfunc(): pass
def count(wrapped): def inner(*args, **kwargs): inner.counter += 1 return wrapped(*args,
**kwargs) inner.counter = 0 return inner @count def myfunc(): pass
>>> myfunc() >>> myfunc() >>> myfunc() >>> myfunc.counter 3
import time def timer(wrapped): def inner(*args, **kwargs): t = time.time()
ret = wrapped(*args, **kwargs) print(time.time()-t) return ret return inner @timer def myfunc(): print(‘so example!’)
import time def timer(wrapped): def inner(*args, **kwargs): t = time.time()
ret = wrapped(*args, **kwargs) print(time.time()-t) return ret return inner @timer def myfunc(): print(‘so example!’)
>>> myfunc() so example! 4.0531158447265625e-06
Colton Myers @basepi
[email protected]
! ! http://bit.ly/dec-pycon-2014 Thanks!