Python Decorator Tutorial – Chaining Decorators, Python Pie Syntax

1. Python Decorator – Objective

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

2. 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.

3. 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()

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

Hello

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

Now let’s see each part of the syntax one by one. Also, Read Recursion in Python.

a. 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.

b. 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.

c. 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()

$

Hello

$

Before Proceeding Read Python Functions with Syntax and Examples.

4. 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)

$

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()

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)

$

Hello

$

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

>>> sayhello()

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.

5. 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().

a. 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)

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))

None

Now, let’s assign and call.

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

0.6666666666666666

>>> divide(2,0)

Can’t divide by 0!

Problem Solved.

b. *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)

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.

6. Pie Syntax in Python

First Go through Python Syntax | The Best Tutorial to Python Syntax

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()

$$

Hello

$$

Python Interview Questions

7. 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()

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

########

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.

8. 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.
Also See:

Leave a Reply

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

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.