
A primer on Python decorators - midas
http://www.thumbtack.com/engineering/a-primer-on-python-decorators/
======
DasIch
There is a small mistake in the post: decorators cannot be arbitrary
expressions. Something like @Foo(spam).bar fails with an unhelpful
SyntaxError, something that everyone who designs complex APIs will probably
encounter at some point. This is a restriction which is in place because Guido
didn't like arbitrary expressions as decorators.

~~~
enaeseth
Oh, Guido. Thanks, I've updated the post.

------
jmurley
Thanks, but my favorite explanation of decorators is still this stackoverflow
answer (see the second answer)

[http://stackoverflow.com/questions/739654/understanding-
pyth...](http://stackoverflow.com/questions/739654/understanding-python-
decorators)

~~~
Aissen
Direct link: <http://stackoverflow.com/a/1594484/378825>

------
lhnz
What I really want to know is how a framework such as Flask uses a decorator
for the route. How is the correct function picked for a particular route that
is defined against the decorator? (Maybe I'm completely misunderstanding
this...)

~~~
gostevehoward
Some might say it's a dangerous abuse of decorators.

Since decorators (generally) run at module load time, any stateful decorator
(usually) implies the use of global mutable state, which is (considered by
many to be) the root cause of much bad design, convoluted flow, limited
reusability and untestability. This is perhaps why most decorators in the
standard library are pure (off the top of my head).

This is well beyond the scope of the post but an important and often
overlooked point in my opinion.

~~~
axiak
In flask, the app.route decorator mutates the app object. There are no globals
necessary. Use of globals to maintain state is an orthogonal issue to use of
decorators to update registries.

~~~
gostevehoward
Well either the app object is global, in which case you've got global mutable
state, or you're defining your handler functions later than module load time,
which is pretty uncommon practice.

~~~
wahnfrieden
You can use the return value of some function as a decorator, as a way to
avoid global state and tie the decorator to a given instance of your routing
object. (I don't know flask, but this isn't a limitation of python)

------
ceol
For some reason, decorators didn't click for me until now. I've been parroting
the standard decorator logic (e.g. returning a function) without knowing why.

Thanks!

------
gbog
To authors: I would avoid try except in this code snippet, a simple if else is
more explicit. I would also avoid a = b = c statement. One line per statement
is better most of the time.

~~~
samdk
The Python community generally advocates an "it's easier to ask for
forgiveness than permission" coding style. When faced with a condition of the
form "if condition a holds, do b, else c", it's very often a better idea to do
"let's try b, and do c in case b fails because condition a didn't hold".

In this case it's better because you can avoid computing an extra hash of the
object in cases where it's already a key of the dictionary. This may seem like
a silly optimization, but it can very easily add up if you're accessing
existing elements most of the time--I once had a bit of code that went 60x
faster when I replaced an if-else with a try-except.

In other cases it can be even more beneficial. Say you're opening a file. One
approach to avoid errors would be to check if a file exists first. This is
error-prone because the file might cease to exist in between the 'if' and the
'open' statements, and now you have no code written to handle the error. Using
try-except will ensure that you actually handle the error intelligently.

This isn't to say there's never a good reason to use an 'if' to check things,
just that if you can do it in one step instead of two, one is usually better.

~~~
jmlane
Out of curiosity, in a situation where you are doing negligible condition
testing, is try–except still considered "more performant" than if–else flow
control? For some reason (and I'm going to do so reading to get clarity on
this point), I have had the silly notion that throwing and handling exceptions
can be costly.

~~~
sateesh
I don't think the try-except is 'more performant'. At least from the below
benchmark test it doesn't seem to be so.

    
    
      >>> from timeit import timeit
      >>> timeit(setup='x=dict([(i,i*2) for i in range(10)])',stmt=
           """
              if 20 in x:
                 pass""")
    
      0.07420943164572691
    
      >>> timeit(setup='x=dict([(i,i*2) for i in range(10)])',
           stmt="""
                   try:
    		  x[20]
                   except KeyError:
    		  pass""")
    
      1.1514457843105674

~~~
koenigdavidmj
I am on my phone and cannot test this, but the try:except: construct is
optimised for the non-exceptional path. The latter is probably faster for x[0]
than x[20].

------
steamboiler
Nice description. I'd suggest explaining how decorators that accept arguments
(i.e. @memcached('some-arg')) work lest it befuddle some beginner. It is not
straightforward (the first argument of a decorator is the function being
decorated).

------
simon_weber
For those who prefer video, Dave Brondsema gave an excellent talk on
decorators and custom context managers at PyCon:
<http://pyvideo.org/video/883/decorators-and-context-managers>.

------
stock_toaster
Nice description of method decorators. Didn't touch on class decorators, or
decorators that can decorate both classes _and_ methods (arguably very ugly
wrapper functions that return decorators that decorate).

~~~
sudhirj
Wrote a post a while ago that talks about them -
<http://hangar.runway7.net/decorators-wrappers-python>

------
oberon
For an interesting use of Python decorators - event handling - have a look at
Decovent on pypi <http://pypi.python.org/pypi/Decovent>.

~~~
timClicks
This is similar to how Pyglet does things:
<http://pyglet.org/doc/programming_guide/hello_world.html>

------
jdwhit2
Great primer, reading it gives a good sense for why the decorator syntax was
made and the potential uses. Is this part of a wider series you are running?

~~~
enaeseth
Thanks! It is indeed the first in a series of Python posts that will be on the
Thumbtack engineering blog.

~~~
zackattack
Can you please email all future posts to me? ;p zackster@gmail.com -- I don't
use RSS any more, and I'd hate to miss them.

~~~
zackattack
Or just let me use Feedburner to subscribe via email.

------
kelvin0
Love using Python, but still haven't got around to using decorators yet. I
probably didn't understand the concept properly until now ...

~~~
gbog
You could say decorators are for the code that you want to put inside a
function but that don't really belong to its logic. The memoization example
shows that: if you have memoization logic inside the function it works but you
should feel that there are two disjoint logics at works.

------
emboss
Minor nitpicking:

> unlike in Java, you can also call a class method on an instance

It's possible in Java, too. It's just considered bad practice.

Still, a very nice read!

~~~
gbog
I'd say if you have to do that, it is most likely because you have a design
problem somewhere. It is a code smell in Python as well.

