Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Decorators: A Powerful Weapon in Your Python Arsenal by Colton Myers
Search
PyCon 2014
April 11, 2014
10
1.6k
Decorators: A Powerful Weapon in Your Python Arsenal by Colton Myers
PyCon 2014
April 11, 2014
Tweet
Share
More Decks by PyCon 2014
See All by PyCon 2014
Postgres Performance for Humans by Craig Kerstiens
pycon2014
29
3.5k
Technical Onboarding, Training, and Mentoring by Kate Heddleston and Nicole Zuckerman
pycon2014
1
2.1k
"My big gay adventure. Making, releasing and selling an indie game made in python." by Luke Miller
pycon2014
2
1.4k
Farewell and Welcome Home, Python in Two Genders by Naomi_Ceder
pycon2014
1
680
Deliver Your Software in an Envelope by Augie Fackler and Nathaniel Manista
pycon2014
1
510
Hitchhikers Guide to Free and Open Source Participation by Elena Williams
pycon2014
6
1.1k
Localization Revisted (aka. Translations Evolved) by Ruchi Varshney
pycon2014
0
640
Smart Dumpster by Bradley E. Angell
pycon2014
0
450
Software Engineering for Hackers: Bridging the Two Solitudes by Tavish Armstrong
pycon2014
0
680
Featured
See All Featured
How to train your dragon (web standard)
notwaldorf
73
5.2k
The Cult of Friendly URLs
andyhume
74
5.7k
Raft: Consensus for Rubyists
vanstee
132
6.3k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
322
20k
Building an army of robots
kneath
300
41k
Designing for humans not robots
tammielis
248
25k
VelocityConf: Rendering Performance Case Studies
addyosmani
320
23k
Learning to Love Humans: Emotional Interface Design
aarron
267
39k
How GitHub (no longer) Works
holman
304
140k
Atom: Resistance is Futile
akmur
259
25k
Product Roadmaps are Hard
iamctodd
44
9.7k
Large-scale JavaScript Application Architecture
addyosmani
504
110k
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!