By Md. Sabuj Sarker | 12/19/2017 | General |Intermediate

Understanding and Working With Decorators in Python

Understanding and Working With Decorators in Python

We like sugar for its sweetness, and in Python we like syntactic sugar a lot. Syntactic sugar in Python makes programming a lot of fun, and decorators in Python are a kind of syntactic sugar. Decorators are just plain old functions that accept another function/callable and return another function/callable. In this time of inputting one function or callable and returning another we do some customization so that we perform some task before and after calling the original function or callable.

A decorator can decorate a class too, but in this guide we will be limited to function decoration. In a later guide or article I will discuss more advanced topics along with class decorators.

Before You Begin

You are advised not to move forward in this guide if you are a beginner Python programmer. Also you should fully grasp the concept that functions in Python are first class objects and they can be passed around as function arguments. Please check the following list before you begin.

  • You need to have Python 3 installed and properly configured on your system
  • You should have familiarity in working with command line on your system
  • It is also expected that you have advanced Python programming experience

Preparing the Environment and Necessary Tools

To keep our python files in which we are going to write code, create or choose a directory. Create a Python file named dec.py inside of it.

To edit code you can use any plain text editor, code editor, or IDE that supports Python. To run the dec.py script I suggest you to use command line.

The Problem We Want to Solve

Let's say that we have a function called say_something() whose sole responsibility is to print whatever is passed to it as the first and the only argument.

def say_something(what):
   print(what)

And we also have a lot of other such functions that do some special formatting before printing what we pass to them. Now, what if we want to see what function is printing what on the screen? We need the name of the function and a colon followed by a new line and then the output from the function and then again another new line.

We can do this just by writing two lines of code inside say_something() like below:

def say_something(what):
   print("say_something():")
   print(what)
   print()

Stop! Now, we need to do that for a whole bunch of other functions that we have. No, a big NO! Again, we do not want to modify the original functions. We want every function to do its responsibility the proper way without doing anything extra.

Then we have another way. We can put those two lines before and after every function call.

This will create a mess of code. We don't want that either. Then what do we do?

Functions that Return Functions

You should have worked with inner functions before or at least heard about them. So, let's create a function that will return a function and make that inner function responsible for printing the name of the function that is being passed.

def return_function(a_fun):
   def inner_fun(*args, **kwargs):
       print(a_fun.__name__ + ":")
       a_fun(*args, **kwargs)
       print()
   return inner_fun

def say_something(what):
   print(what)

return_function can take a function as the argument and return an inner function. The inner function does the additional tasks along with calling the passed function in the middle of the tasks.

Now, let's pass say_something to that function and store the return value as a variable called say_something (the same as the function identifier).

say_something = return_function(say_something)
say_something("I want to stay silent")
say_something("I told you that I wanted to stay silent!!")

So, the complete code looks like the following:

def return_function(a_fun):
   def inner_fun(*args, **kwargs):
       print(a_fun.__name__ + ":")
       a_fun(*args, **kwargs)
       print()
   return inner_fun

def say_something(what):
   print(what)

say_something = return_function(say_something)
say_something("I want to stay silent")
say_something("I told you that I wanted to stay silent!!")

Execute the dec.py with the following command:

$ python3 dec.py

You will see the following output:

say_something:
I want to stay silent

say_something:
I told you that I wanted to stay silent!!

Our purpose is well server without us needing to modify the original source code of the function that we want to treat like this.

The Cleaner Way

The line say_something = return_function(say_something) does not look clean. What it is doing is that removing say_something function object from the scope and creating another variable with the same name that points to another function that holds the original say_something function and does some extra work. As a result anyone using or calling say_something is logging the work of it without knowing or without doing extra work.

We need a cleaner way. That cleaner way is the syntactic sugar of decorators. Decorators are an at symbol '@' and the function name, that returns another function, followed by a function definition. See the example below to clarify the concept.

@return_function
def say_something(what):
   print(what)

Removing say_something = return_function(say_something) and decorating the say_something function definition with @return_function keeps everything the same and we get the same result as before with nicer syntax, cleaner code.

Let's rename return_function to print_decorator

So, our final code should look like the following.

def print_decorator(a_fun):
   def inner_fun(*args, **kwargs):
       print(a_fun.__name__ + ":")
       a_fun(*args, **kwargs)
       print()
   return inner_fun

@print_decorator
def say_something(what):
   print(what)

say_something("I want to stay silent")
say_something("I told you that I wanted to stay silent!!")

Run it to verify that everything is as before and our output is identical.

Conclusion

Decorators are a good way of writing cleaner code but not compromising our intentions. When you work with different frameworks in Python you will see how much they use decorators. Next time you see that cute syntax of decorators, I hope you will not be confused and use it with pleasure. I also hope that you will create decorators when you need to do so, to do something extra with the function calls. There are some more advanced aspects of decorators that I will cover in some other guide, article or tutorial. Keep coding in Python and never forget to practice over and over again.

By Md. Sabuj Sarker | 12/19/2017 | General

{{CommentsModel.TotalCount}} Comments

Your Comment

{{CommentsModel.Message}}

Recent Stories

Top DiscoverSDK Experts

User photo
3355
Ashton Torrence
Web and Windows developer
GUI | Web and 11 more
View Profile
User photo
3220
Mendy Bennett
Experienced with Ad network & Ad servers.
Mobile | Ad Networks and 1 more
View Profile
User photo
3060
Karen Fitzgerald
7 years in Cross-Platform development.
Mobile | Cross Platform Frameworks
View Profile
Show All
X

Compare Products

Select up to three two products to compare by clicking on the compare icon () of each product.

{{compareToolModel.Error}}

Now comparing:

{{product.ProductName | createSubstring:25}} X
Compare Now