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.7k
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.6k
Farewell and Welcome Home, Python in Two Genders by Naomi_Ceder
pycon2014
1
740
Deliver Your Software in an Envelope by Augie Fackler and Nathaniel Manista
pycon2014
1
550
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
700
Smart Dumpster by Bradley E. Angell
pycon2014
0
530
Software Engineering for Hackers: Bridging the Two Solitudes by Tavish Armstrong
pycon2014
0
740
Featured
See All Featured
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
8
790
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
5
210
Building an army of robots
kneath
306
45k
Large-scale JavaScript Application Architecture
addyosmani
512
110k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
3k
RailsConf 2023
tenderlove
30
1.1k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
53
2.8k
Optimising Largest Contentful Paint
csswizardry
37
3.3k
A Tale of Four Properties
chriscoyier
160
23k
Product Roadmaps are Hard
iamctodd
PRO
54
11k
GraphQLの誤解/rethinking-graphql
sonatard
71
11k
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!