This Tutorial was written using Python 3.6
In this tutorial, I am going to be demonstrating the wonderful concept that is Python decorators. We’ll look at the various attributes of Python that make writing your own decorators fairly simple and then we’ll cover some cool examples as to how you can use decorators to improve the performance of some types of applications.
If you prefer, feel free to check out the video version of this course, like and subscribe to my channel for more Python related video content!
Decorators are a pretty cool concept that allow you to decorate functions and classes with a decorator and a new, transformed function or class will be returned. If you are coming from a Java background then you may have used decorators a lot in frameworks such as Spring and Jersey.
If you come from a pure Python background then you may have seen decorators
feature in frameworks such as flask. In flask, you would typically define a
function and then decorate it with things such as
in order to specify that this is the function that will be executed whenever the
/my-endpoint is called from your app.
Let’s take a look at a real life example. In the below code we define a very
simple Flask based program. This program features only one function
def hello():, right above this function declaration you should see
@app.route("/") which is an example of an
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!"
If we were to run this program then you would see a very simple service start up
http://localhost:5000 and whenever you navigated to that page in the
browser you would would see that our
hello() function is called and
Hello World! is printed in our browser.
First Class Citizens
So how do we write our own decorators? We’ll it turns out it’s surprisingly simple. In Python functions and classes are considered first class citizens. This essentially means that they can be treated much the same as you would treat variables or objects within your python applications. We can do cool things like pass one function as an argument into another function like so:
def myFunction(): print("Hello") def anotherFunction(function): print(function) anotherFunction(myFunction)
When you run this python program you should see the following output:
$ python3.6 decorators.py <function myFunction at 0x100562e18>
Instead of printing out hello, it prints out that the
myFunction() that we
anotherFunction() is of type
function as well as it’s name and
it’s address in memory.
With functions being classed as objects within Python, we can start to do cool things such as nested functions.
Imagine you had an application that parsed a message that was either in a
format or an
XML format, you could define a
parse(type, message) function
that featured 2 nested functions, it would then execute and return the results
from the appropriate function depending on the format.
def parse(type, message): def xml(message): print("Parsing XML") def json(message): print("Parsing JSON") if type == 'json': return json(message) else: return xml(message)
When you run this and pass in
type='json' and your message, it should return
>>> parse(type='json', message='my message') json
A Simple Decorator
Due to the fact functions are a first class citizen in python, we can also
manipulate them in much the same way that we would manipulate variables. Let’s
define a very simple decorator that will return a new function and wrap it in
two print statements. This decorator will be called
def mutate(method): def newmethod(*args, **kwargs): print("Executing Method") method(*args, **kwargs) print("Finished Executing Method") return newmethod @mutate def myFunction(): print("Hello") myFunction()
You’ll notice that in the above code we have been able to define the
newmethod()function within the body of the
mutate()function. This is one of the benefits of functions being a first class citizen.
You should see that when we execute the above code you should see the following output:
$ python3.6 decorators.py Executing Method Hello Finished Executing Method
Our call to
myFunction() has successfully triggered our mutate decorator,
which has modified our original function and returned a new function which
contains our desired print statements.
Taking it further
This is just a simple example of how you can write your own python decorators, but there are hundreds of other uses for decorators within your Python applications.
For a pretty cool list of all the various different things you can do with decorators I suggest you check out the Python Decorator Library.
If you have seen my post on memoization which is a cool performance optimization technique used in recursive programs then you might like their decorator example which automatically memoizes the results of said function for you: Python Decorator Memoization
When we utilize decorators in the above fashion you may notice an unintended side-affect where the function has been renamed to that of the decorator.
>>> repr(myFunction) '<function mutate.<locals>.newmethod at 0x1022b8e18>'
If we decorate hundreds of functions with this
@mutate decorator then you may
find that your programs become slightly harder to debug as tools that are
specifically designed for introspection will throw back incorrect function names
like the above example.
In order to fix this particular side-effect, we can look to the
module which is built-in to Python. We add
from functools import * to the top
of our file and within our decorator, we add a second
@wraps() decorator which
takes in the original function as it’s parameter. If we update our example code
above to include this fix, it should then look like this:
from functools import * def mutate(method): @wraps(method) def newmethod(*args, **kwargs): print("Executing Method") method(*args, **kwargs) print("Finished Executing Method") return newmethod @mutate def myFunction(): print("Hello") myFunction()
And when we again call
repr on our function you’ll see it outputs the correct
>>> from main import * Executing Method Hello Finished Executing Method >>> repr(myFunction) '<function myFunction at 0x1022d3e18>'
In this tutorial, we managed to cover a number of key things you’ll need to know when working with decorators in Python. We covered the fact that in Python, functions are considered first class citizens, we also looked at how you could nest functions within other functions before finally looking at how you could create your own very simple decorators.
Hopefully, you found this tutorial on Python decorators useful! If you require further assistance then please feel free to let me know in the comments section below or by tweeting me: @Elliot_F.