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
630
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
750
Smart Dumpster by Bradley E. Angell
pycon2014
0
600
Software Engineering for Hackers: Bridging the Two Solitudes by Tavish Armstrong
pycon2014
0
790
Featured
See All Featured
Bash Introduction
62gerente
615
220k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.6k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.5k
Marketing to machines
jonoalderson
1
5.9k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
2
370
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
370
Designing for humans not robots
tammielis
254
26k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
530
The #1 spot is gone: here's how to win anyway
tamaranovitovic
4
1.2k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
360
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
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!