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 Ar...
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.6k
Technical Onboarding, Training, and Mentoring by Kate Heddleston and Nicole Zuckerman
pycon2014
1
2.3k
"My big gay adventure. Making, releasing and selling an indie game made in python." by Luke Miller
pycon2014
2
1.5k
Farewell and Welcome Home, Python in Two Genders by Naomi_Ceder
pycon2014
1
720
Deliver Your Software in an Envelope by Augie Fackler and Nathaniel Manista
pycon2014
1
530
Hitchhikers Guide to Free and Open Source Participation by Elena Williams
pycon2014
6
1.2k
Localization Revisted (aka. Translations Evolved) by Ruchi Varshney
pycon2014
0
690
Smart Dumpster by Bradley E. Angell
pycon2014
0
510
Software Engineering for Hackers: Bridging the Two Solitudes by Tavish Armstrong
pycon2014
0
720
Featured
See All Featured
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
Code Reviewing Like a Champion
maltzj
521
39k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.8k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
49
2.3k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
193
16k
Why Our Code Smells
bkeepers
PRO
336
57k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
21
2.5k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Writing Fast Ruby
sferik
628
61k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
Large-scale JavaScript Application Architecture
addyosmani
511
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!