Decorators in Python will make your code so much better
Analyze, test, and re-use your code with little more than an @ symbol
Before anything else: higher-order functions
In a nutshell, decorators are a neat way to handle higher-order functions. So let’s look at those first!
Functions returning functions
Say you have one function,greet()— it greets whatever object you pass it. And let’s say you have another function,simon()— it inserts “Simon” wherever appropriate. How can we combine the two? Think about it a minute before you look below.
The output is’Hello, Simon!'. Hope that makes sense to ya!
Of course, we could have just calledgreet(“Simon”). However, the whole point is that we might want to put “Simon” into many different functions. And if we don’t use “Simon” but something more complicated, we can save a whole lot of lines of code by packing it into a function likesimon().
Functions inside other functions
We can also define functions inside other functions. That’s important because decorators will do that, too! Without decorators it looks like this:
The functionrespect()returns a function;respect(“yes”)returns the congrats function,respect(“brother”)(or some other argument instead of"brother") returns the insult function. To call the functions, enterrespect(“yes”)()andrespect(“brother”)(), just like a normal function.
Got it? Then you’re all set for decorators!
The ABC of Python decorators
Functions with an @ symbol
Let’s try a combination of the two previous concepts: a function that takes another function and defines a function. Sounds mind-boggling? Consider this:
The last line ensures that we don’t need to callstartstop(roll)()anymore;roll()will suffice. Do you know what the output of that call is? Try it yourself if you’re unsure!
Now, as a very good alternative, we could insert this right after definingstartstop():
This does the same, but gluesroll()tostartstop()at the onset.
Added flexibility
Why is that useful? Doesn’t that consume exactly as many lines of code as before?
In this case, yes. But once you’re dealing with slightly more complicated stuff, it gets really useful. For once, you can move all decorators (i.e. thedef startstop()part above) into its own module. That is, you write them into a file calleddecorators.pyand write something like this into your main file:
In principle, you can do that without using decorators. But this way it makes life easier because you don’t have to deal with nested functions and endless bracket-counting anymore.
You can also nest decorators:
Note that we haven’t definedexectime()yet, but you’ll see it in the next section. It’s a function that can measure how long a process takes in Python.
This nesting would be equivalent to a line like this:
Bracket counting is starting! Imagine you had five or six of those functions nested inside each other. Wouldn’t the decorator notation be much easier to read than this nested mess?
You can even use decorators on functions thataccept arguments. Now imagine a few arguments in the line above and your chaos would be complete. Decorators make it neat and tidy.
Finally, you can even add argumentsto your decorators— like@mydecorator(argument). Yeah, you can do all of this without decorators. But then I wish you a lot of fun understanding your decorator-free code when you re-read it in three weeks…
Applications: where decorators cut the cream
Now that I’ve hopefully convinced you that decorators make your life three times easier, let’s look at some classic examples where decorators are basically indispensable.
Measuring execution time
Let’s say we have a function calledwaste time()and we want to know how long it takes. Well, just use a decorator!
A dozen lines of code and we’re done! Plus, you can usemeasuretime()on as many functions as you want.
Slowing code down
Sometimes you don’t want to execute code immediately but wait a while. That’s where a slow-down decorator comes in handy:
Callingwakeup()makes lets you take a 5-minute break, after which your console reminds you to get back to work.
Testing and debugging
Say you have a whole lot of different functions that you call at different stages, and you’re losing the overview over what’s being called when. With a simple decorator for every function definition, you can bring more clarity. Like so:
There is a more elaborate examplehere. Note, though, that to understand that example, you’ll have to checkhow todecorate functions with arguments. Still, it’s worth the read!
Reusing code
This kinda goes without saying. If you’ve defined a functiondecorator(), you can just sprinkle@decoratoreverywhere in your code. To be honest, I don’t think it gets any simpler than that!
Handling logins
If you have functionalities that should only be accessed if a user is logged in, that’s also fairly easy with decorators. I’ll refer you to thefull examplefor reference, but the principle is quite simple: first, you define a function likelogin_required(). Before any function definition that needs logging in, you pop@login_required. Simple enough, I’d say.
Syntactic sugar — or why Python is so sweet
It’s notlike I’m notcritical of Pythonor notusingalternative languageswhere it’s appropriate. But there’s a big allure to Python: it’s so easy to digest, even when you’re not a computer scientist by training and just want to make things work.
If C++ is an orange, then Python is a pineapple: similarly nutritious, but three times sweeter. Decorators are just one factor in the mix.
But I hope you’ve come to see why it’s such a big sweet-factor. Syntactic sugar to add some pleasure to your life! Without health risks, except for having your eyes glued on a screen.
This article was written byAri Jouryand was originally published onTowards Data Science. You can read ithere.
Story byAri Joury
Get the TNW newsletter
Get the most important tech news in your inbox each week.