Nearby lessons

116 of 159

Python - Decorators

📌 What You Will Learn
  • Understand what a decorator function is
  • Extend the functionality of an existing function without modifying it
  • Use the @decorator syntax
  • Call a function with and without a decorator
  • Apply multiple decorators using decorator chaining

What is a Decorator?

A Decorator is a function which can take a function as an argument, extend its functionality, and return a modified function with the extended functionality.

The main objective of decorator functions is to extend the functionality of existing functions without modifying those functions.

The Problem

Consider the following function:

🐍Code Cell
1def wish(name):
2 print("Hello", name, "Good Morning")
Output
No output captured.

Explanation

This function always prints the same output for any name:

🐍Code Cell
1Hello Durga Good Morning
2Hello Ravi Good Morning
3Hello Sunny Good Morning
Output
No output captured.

The Requirement

Now we want to modify this function so that it provides a different message when the name is Sunny.

We can do this without touching the wish() function by using a decorator.

Example: A Decorator for the wish() Function

🐍Code Cell
1def decor(func):
2 
3 def inner(name):
4 if name == "Sunny":
5 print("Hello Sunny Bad Morning")
6 else:
7 func(name)
8 
9 return inner
10 
11 
12@decor
13def wish(name):
14 print("Hello", name, "Good Morning")
15 
16 
17wish("Durga")
18wish("Ravi")
19wish("Sunny")
Output
Hello Durga Good Morning
Hello Ravi Good Morning
Hello Sunny Bad Morning

Explanation

  • decor is a decorator function. It takes func as an argument and returns inner.
  • inner() checks whether the name is Sunny. If it is, it prints a different message; otherwise it calls the original func(name).
  • @decor above wish tells Python to pass wish through decor. Whenever wish() is called, the extended version executes automatically.

The Inner Function Pattern

Notice the structure of a typical decorator:

🐍Code Cell
1def decor(func):
2 
3 def inner(*args):
4 # add extra behaviour
5 func(*args)
6 
7 return inner
Output
No output captured.

Explanation

The decorator decor takes the original function func as input, defines an inner function that adds the extra behaviour, and returns inner.

From that point onwards, calling the original function name executes the inner function instead.

How to Call the Same Function With and Without a Decorator

If we do not use the @decor syntax, the decorator is not applied automatically.

In that case we can manually pass the function to the decorator to obtain the extended version.

🐍Code Cell
1def decor(func):
2 
3 def inner(name):
4 if name == "Sunny":
5 print("Hello Sunny Bad Morning")
6 else:
7 func(name)
8 
9 return inner
10 
11 
12def wish(name):
13 print("Hello", name, "Good Morning")
14 
15 
16decorfunction = decor(wish)
17 
18wish("Durga")
19wish("Sunny")
20 
21decorfunction("Durga")
22decorfunction("Sunny")
Output
Hello Durga Good Morning
Hello Sunny Good Morning
Hello Durga Good Morning
Hello Sunny Bad Morning

Explanation

  • wish("Durga") and wish("Sunny") call the original function directly, so the decorator is not executed.
  • decorfunction = decor(wish) manually creates the extended version.
  • decorfunction("Durga") and decorfunction("Sunny") call the extended version, so the decorator is executed.

Example 2: smart_division

This decorator prevents a ZeroDivisionError when dividing by zero.

🐍Code Cell
1def smart_division(func):
2 
3 def inner(a, b):
4 print("We are dividing", a, "with", b)
5 if b == 0:
6 print("OOPS...cannot divide")
7 return
8 else:
9 return func(a, b)
10 
11 return inner
12 
13 
14@smart_division
15def division(a, b):
16 return a / b
17 
18 
19print(division(20, 2))
20print(division(20, 0))
Output
We are dividing 20 with 2
10.0
We are dividing 20 with 0
OOPS...cannot divide
None

Explanation

Without the decorator, division(20, 0) would raise a ZeroDivisionError.

With the @smart_division decorator, the inner() function checks whether b is 0 before calling the original division(), so the program never crashes.

Decorator Chaining

We can define multiple decorators for the same function. All these decorators together form Decorator Chaining.

🐍Code Cell
1@decor1
2@decor
3def num():
Output
No output captured.

How Chaining Works

When multiple decorators are applied, the decorator closest to the function works first, and then the outer decorator works on its result.

In the example above, decor is applied first, then decor1 is applied to the result.

Example: Decorator Chaining with num()

🐍Code Cell
1def decor1(func):
2 
3 def inner():
4 x = func()
5 return x * x
6 
7 return inner
8 
9 
10def decor(func):
11 
12 def inner():
13 x = func()
14 return 2 * x
15 
16 return inner
17 
18 
19@decor1
20@decor
21def num():
22 return 10
23 
24 
25print(num())
Output
400

Explanation

The decor decorator runs first: it calls num(), gets 10, and returns 2 * 10 = 20.

Then decor1 runs on that result: it calls the already-decorated function, gets 20, and returns 20 * 20 = 400.

Therefore, the final output is 400.

Demo Program for Decorator Chaining

🐍Code Cell
1def decor(func):
2 
3 def inner(name):
4 print("First Decor(decor) Function Execution")
5 func(name)
6 
7 return inner
8 
9 
10def decor1(func):
11 
12 def inner(name):
13 print("Second Decor(decor1) Execution")
14 func(name)
15 
16 return inner
17 
18 
19@decor1
20@decor
21def wish(name):
22 print("Hello", name, "Good Morning")
23 
24 
25wish("Durga")
Output
Second Decor(decor1) Execution
First Decor(decor) Function Execution
Hello Durga Good Morning

Explanation

The output confirms the order of chaining:

  1. decor1 (the outer decorator) starts first.
  2. It calls the decor-wrapped function, which prints the first message.
  3. Finally the original wish() body executes.
📝 Key Takeaways
  • A decorator is a function that takes a function as an argument and returns an extended version of it
  • The @decorator syntax automatically passes the function below it to the decorator
  • A decorator typically defines an inner function that adds extra behaviour
  • Calling the original function name after decoration executes the extended version
  • Multiple decorators on one function form a decorator chain; the inner decorator works first

🧠 Test Your Knowledge

10 Questions
Progress: 0 / 10