Python Decorator Tutorial – Chaining Decorators, Python Pie Syntax

Python course with 57 real-time projects - Learn Python

In this Python Decorator tutorial, we will study what is a decorator in Python and why we use a Python Nested Functions.

Along with this, we will learn Python Decorators with Parameters and Python Pie Syntax. At last, we will study Chaining Decorators in Python programming language.

In Python, a function is a first-class object. This means that you can pass it around with absolute ease.

You can return it, and even pass it as an argument to another. You can also nest a python function inside another.

So, let’s start the Python Decorator Tutorial.

Python Decorator Tutorial - Chaining Decorators, Python Pie Syntax

Python Decorator Tutorial – Chaining Decorators, Python Pie Syntax

What is Python Decorator?

Python Decorator function is a function that adds functionality to another, but does not modify it. In other words, Python Decorator wraps another function.

This is like a gift wrapper in real life. Also, this is called metaprogramming, because a part of the program tries to modify another at compile time.

In the rest of the lesson, we will see the python syntax of a Python decorator in detail. This is useful in cases when you want to add functionality to a function, but don’t want to modify it for the same.

Let’s take a look.

A Simple Python Decorator

Talking about Python decorator for the first time can be confusing. So we begin with a very simple example with no arguments.

Take this code.

>>> def decor(func):
	def wrap():
		print("$$$$$$$$$$$$$$$$$$$$$$")
		func()
		print("$$$$$$$$$$$$$$$$$$$$$$")
	return wrap
>>> def sayhello():
          print("Hello")
>>> newfunc=decor(sayhello)
>>> newfunc()

Output

$$$$$$$$$$$$$$$$$$$$$$

Hello

$$$$$$$$$$$$$$$$$$$$$$

Now let’s see each part of the syntax one by one.

1. Python Decorator Function

First, we define a simple function sayhello() that prints out “Hello”. Now, we’ll define the decorator function in Python.

You can call this anything; it doesn’t have to be ‘decor’. This is a higher order function. Note that the relative order of these functions does not matter.

You could define sayhello() before defining decor(), and it wouldn’t make a difference. Let’s discuss the decor function in detail.

def decor(func):

The first thing to notice here is that it takes a function as an argument. This is the function that we want to decorate.

We want to call it func; you may want to call it something else. Inside this function, we nest a function, and we call it wrap().

Again, you can call it anything you want.

2. The nested wrap function

It is inside this function that we put our extra functionality, and also call the function to be decorated.

def wrap():
           print("$$$$$$$$$$$$$$$$$$$$$$")
           func()
           print("$$$$$$$$$$$$$$$$$$$$$$")

Here, we used some print statements. It could have been anything else too, like an if-block.

Finally, we make the decor() function return the wrap function.

return wrap

Why do we do this? We’ll discuss this further in this lesson.

3. Assigning and Calling

Finally, we assign this Python decorator to a variable and pass the function to be decorated as an argument to the decorating function.

newfunc=decor(sayhello)

Then, we call the function using parentheses after the variable to which we assign the decorators in python.

newfunc()

However, you can also assign this to the function to be decorated itself. This will reassign it. Let’s see that as well.

>>> def sayhello():
          print("Hello")
>>> def decor(func):
	def wrap():
		print("$")
		func()
		print("$")
	return wrap
>>> sayhello=decor(sayhello)
>>> sayhello()

Output

$

Hello

$

Why use a Python Nested Function?

When I was attempting to understand Python decorator, this question totally confused me. Why do we use the wrap function, and then return it?

Couldn’t we simply write the code inside the decor function? So I ended up on the interpreter, trying it out.

>>> def decor(func):
         print("$")
         func()
         print("$")
>>> def sayhello():
         print("Hello")

Here, we wrote the extra functionality right inside our decor() function. Now, let’s try to assign it to a variable.

>>> newfunc=decor(sayhello)

Output

$

Hello

$

Woah. Why did it print it out? This is because decor() calls a function (here, func) instead of returning a value.

When we use wrap (or whatever you’d call it), and then return it, we can store it in a variable. Then, we can use that name to call the decorated function whenever we want.

Now let’s call the newfunc() function.

>>> newfunc()

Output

Traceback (most recent call last):File “<pyshell#70>”, line 1, in <module>

newfunc()

TypeError: ‘NoneType’ object is not callable

As you can see, since decor did not return a value, the line of assignment did not assign the decorated function to newfunc. This is why it isn’t callable.

So, it is impossible to access this Python decorator again except for the following method:

>>> decor(sayhello)

Output

$

Hello

$

Finally, let’s try calling our original function sayhello().

>>> sayhello()

Output

Hello

Works perfectly. See, decor() did not modify sayhello().

We use the same example everywhere so you can focus on what’s being explained, and not be invested in trying to understand the code.

Python Decorator with Parameters

So far, we’ve only seen decorators in python with regular print statements. Now, let’s get into the real thing.

To see how decorators fare with parameters, we’ll take the example of a function that divides two values. All that our function does is to return the division of two numbers.

But when we decorate it, we add functionality to deal with the situation where the denomination is 0. Watch how.

>>> def divide(a,b):
          return a/b
>>> def decorator(func):
          def wrapper(a,b):
                 if b==0:
                     print("Can't divide by 0!")
                     return
                 return func(a,b)
          return wrapper

Like you can see, the Python decorator function takes one argument for the function to decorate. The wrapper here takes the same arguments as does the function to decorate.

Finally, we return the function to be decorated instead of calling it. This is because we want to return a value here from the divide() to the wrapper() to the decorator().

1. Python Closure

When we call func, it remembers the value of func from the argument to the function decorator(). This is called closure in Python.

Here’s another example to clear this up.

>>> msg="Hello"
>>> def func1(msg):
         def func2():
              print(msg)
         func2()

>>> func1(msg)

Output

Hello

Also, note that if we called func(a,b) instead of returning it, we’d get this:

>>> divide(2,3)
>>> print(divide(2,3))

Output

None

Now, let’s assign and call.

>>> divide=decorator(divide)
>>> divide(2,3)

Output

0.6666666666666666
>>> divide(2,0)

Output

Can’t divide by 0!

Problem Solved.

2. *args and **kwargs

If you don’t want to type in the whole list of arguments for the two statements, *args and **kwargs will do the trick for you.

>>> def divide(a,b):
         return a/b
>>> def decorate(func):
         def wrapper(*args,**kwargs):
            if args[1]==0:
                 print("Can't divide by 0!")
                 return
              return func(*args,**kwargs)
           return wrapper

>>> divide=decorate(divide)
>>> divide(2,0)

Output

Can’t divide by 0!

See, it works. Actually, *args is a tuple of arguments, and **kwargs is a dictionary of keyword arguments.

Any Doubt yet in Python 3 Decorators? Please ask in comments.

Pie Syntax in Python

If you feel the assignment and calling statements are unnecessary, we’ve got the pie syntax for you.

It’s simple; name the decorating function after the @ symbol, and put this before the function to decorate.

Here’s an example.

>>> @decor
def sayhello():
       print("Hello")
>>> sayhello()

Output

$$

Hello

$$

Chaining Decorators in Python

You don’t have to settle with just one Python decorator. That’s the beauty of the pie syntax. Let’s see how.

>>> def decor1(func):
        def wrap():
               print("$$$$$$$$$$$$$$")
               func()
               print("$$$$$$$$$$$$$$")
        return wrap

>>> def decor2(func):
        def wrap():
               print("##############")
               func()
               print("##############")
        return wrap

Now, let’s define the sayhello() function. We’ll use decor1 and decor2 on this.

>>> @decor1
@decor2
def sayhello():
         print("Hello")

>>> sayhello()

Output

$$$$$$$$$$$$$$

########

Hello

########

$$$$$$$$$$$$$$

Note how the octothorpes (#) are sandwiched by dollars ($)?

This lends us an important piece of information- the order of the decorators in python in the pie syntax matters. Since we used decor1 first, we get the dollars first.

This was all about the Python Decorator Tutorial.

Python Interview Questions on Decorators

  1. What is decorator in Python? Explain with example.
  2. How do you create a decorator in Python?
  3. How do you decorate a class in Python?
  4. Where are Python decorators used?
  5. What do decorators do in Python?

Conclusion

Concluding what we discussed so far in this Python Decorator with arguments simple examples Tutorial, decorators in python help us add extra functionality to a function without modifying it.

We also saw the pie syntax for the same. And now you know- anything that confuses you, you can conquer it by facing it, for you can’t run forever.

Escape isn’t real. See you again.

Did you know we work 24x7 to provide you best tutorials
Please encourage us - write a review on Google

follow dataflair on YouTube

3 Responses

  1. Arun says:

    Hi,
    The tutorial is good but Can you give us some more insight on how the pie syntax works in decorators.
    we just call the decorator name using ‘@’ symbol, the decorator will have outer function & return and inner function & return, so the return of outer function is directly going to call inner function and the return values of inner function where it is stored, all those.
    Because @decor, is just a single word but many things happen inside the flow

    • DataFlair says:

      Hi Arun,
      In the given example, two functions named decor1 and decor2 have been defined that call a function given as an argument. And we used the @ symbol along with the names of the two functions just before the sayhello() function. This decorator calls each of the functions in the order in which they are written. And in the place where the function sayhello() is called, it gets executed once. I hope, you can understand the explanation.

  2. Ajit says:

    Hi ,
    Thanks for deeper explanation of python decorator, In chaining decorators example, func() called in each decorator, that mean func() has called two times. then it should print “Hello” two time. But it print “Hello” only one time. Please explain why only one time print “Hello”

Leave a Reply

Your email address will not be published. Required fields are marked *