Python Decorators

A Python decorator is a special function that lets you modify or extend the behavior of another function or method without changing its actual code. Set additional behavior and apply to a function using decorators.

A decorator is a function that takes another function as input and returns a new function with added functionality.

They can be applied to functions, methods, or even classes, making them versatile tools for modifying behavior dynamically.

Use Cases of Python Decorators

  • Authentication: Check user credentials before allowing access to a function
  • Logging: Automatically log function calls, arguments, and return values
  • Validation: Validate inputs before executing the function
  • Access Control: Restrict access based on roles or permissions
  • APIs: Decorators validate tokens or API keys before allowing access.
  • Enterprise apps: Role-based decorators ensure only managers or admins can perform certain actions.

Example 1

Let us see an example to change the case. Here, we have a decorator convertcase that uppercases the return value of the decorated function demofunction():

def convertcase(func):
  def myinner():
    return func().lower()
  return myinner

@convertcase
def demofunction():
  return "Hello World"

print(demofunction())

Output

hello world

The program begins execution from the very first line of code in the file.

  • First, definitions are processed.
  • Then, decorators are applied at definition time.
  • Actual runtime execution happens when you hit print(demofunction()

By placing @convertcase directly above the function definition, the function demofunction() is being “decorated” with the convertcase() function.

The function convertcase() is the decorator. The function demofunction() is the function that gets decorated.

Flow of Execution in Order

  1. Function Definition:
    • convertcase is defined and stored.
    • Next, demofunction is defined, but because of @convertcase, Python immediately applies the decorator.
  2. Decorator Application:
    • At the moment of definition, demofunction = convertcase(demofunction) is executed.
    • So demofunction now points to myinner.
  3. Print Statement:
    • Finally, Python reaches print(demofunction()).
    • This triggers the call chain we discussed earlier:
      • demofunction() → really myinner()
      • myinner() calls the original demofunction() → “Hello World”
      • .lower() → “hello world”
      • Returned and printed.

Multiple Decorator Calls

In Python, we can call the decorator multiple times, i.e., one decorator @convertcase applied to two functions.

Example 2

Let us see an example. In the code, @convertcase is used once on demo1function() and once on demo2function(). Each function is individually wrapped by the same decorator. So, both functions now return their output in lowercase, but they’re independent of each other.

def convertcase(func):
  def myinner():
    return func().lower()
  return myinner

@convertcase
def demo1function():
  return "Hello World!"

@convertcase
def demo2function():
  return "My first program!"

print(demo1function())
print(demo2function())

Output

hello world!
my first program!

Flow of Execution

  1. Defining the Decorator
  • convertcase(func) is a decorator function.
  • Inside it, another function myinner() is defined, which:
    • Calls the original function func()
    • Converts its return value to lowercase using .lower()
  • Finally, convertcase returns myinner.

So, the decorator essentially wraps the original function with extra behavior (lowercasing the result).

  1. Applying the Decorator

When you write:

@convertcase
def demo1function():
    return "Hello World!"

It is equivalent to:

def demo1function():
    return "Hello World!"
demo1function = convertcase(demo1function)
  • That means demo1function no longer directly refers to the original function.
  • Instead, it now refers to the myinner function returned by convertcase.

The same happens for demo2function.

  1. Calling the Decorated Functions

When you call:

print(demo1function())

Here’s what happens:

  1. demo1function() actually calls myinner().
  2. Inside myinner, the original demo1function() (the one returning “Hello World!”) is executed.
  3. “Hello World!” is returned.
  4. .lower() converts it to “hello world!”.
  5. That lowercase string is returned and printed.

Similarly:

print(demo2function())
  • Calls myinner().
  • Executes original demo2function() → “My first program!”.
  • Converts to lowercase → “my first program!”.
  • Prints the result.

Here’s the flowchart of execution:

@convertcase applied
        ↓
convertcase(demo1function) → returns myinner
        ↓
demo1function = myinner

Execution:
demo1function() → calls myinner()
        ↓
myinner() → calls original demo1function()
        ↓
original demo1function() → returns "Hello World!"
        ↓
.lower() → "hello world!"
        ↓
print → hello world!

Multiple Decorators on one function

We saw multiple calls to a single decorator above, but in Python, we can also use multiple decorators on a single function.

Remember, Python applies decorators from bottom to top, i.e., Decorators are called in the reverse order. The decorator closest to the function gets called first. Therefore, in the below example @addmessage decorator will get called first.

Example 3

Let us see the example:

def convertcase(func):
  def myinner():
    return func().lower()
  return myinner

def addmessage(func):
  def myinner():
    return "Hi " + func() + " How are you doing?"
  return myinner

@convertcase
@addmessage
def demofunction():
  return "John"

print(demofunction())

Output

hi john how are you doing?

Execution flow step by step

1. Decorator stacking

Python applies decorators from bottom to top. So in the code:

@convertcase
@addmessage
def demofunction():
    return "John"

This is equivalent to:

demofunction = convertcase(addmessage(demofunction))

2. First decorator: @addmessage

  • addmessage(demofunction) is called.
  • Inside addmessage, it defines myinner() which returns "Hi " + func() + " How are you doing?".
  • Here, func is the original demofunction (which returns "John").
  • So addmessage(demofunction) returns the function myinner.

At this point, demofunction is replaced with myinner from addmessage.

3. Second decorator: @convertcase

  • Now convertcase(addmessage(demofunction)) is called.
  • Inside convertcase, it defines another myinner() which returns func().lower().
  • Here, func is the function returned by addmessage (the inner one).
  • So convertcase(...) returns its own myinner.

At this point, demofunction is replaced with myinner from convertcase.

4. Execution of print(demofunction())

  • When demofunction() is called, it executes the myinner from convertcase.
  • That calls func().lower(), where func is the myinner from addmessage.

Inside addmessage.myinner():

  • It executes "Hi " + func() + " How are you doing?".
  • Here, func is the original demofunction (which returns "John").
  • So it becomes "Hi John How are you doing?".

Back to convertcase.myinner():

  • It takes that string and applies .lower().
  • Result: "hi john how are you doing?".

If you liked the tutorial, spread the word and share the link and our website, Studyopedia, with others.


For Videos, Join Our YouTube Channel: Join Now


Read More:


Python Tutorial | Learn Python Programming
Python Generators
Studyopedia Editorial Staff
contact@studyopedia.com

We work to create programming tutorials for all.

No Comments

Post A Comment