Python Tutorials
Basic

Data Structure and Data Types

String

List

Tuple

Set

Dictionary

Functions / Methods

Advance

Exception Handling

Python Decorator Functions


Decorator Functions

In Python, decorators are functions that add functionality to existing Python functions without changing the structure of the Python function itself.


Before going to the next, we understand Python some basic concepts.

Python treats everything as an object, including classes and functions. Names are simple identifiers for those objects. Different names can be assigned to the same function object.

Example
def welcome(name):
    print(name)

welcome("John")

hello = welcome
hello("John")
'John'
'John'

The output of both functions welcome() and hello() is the same. The welcome() and hello() refer to the same function object.


Pass Function As an Argument

It is possible to pass functions as arguments to other functions.

A function that takes another function as an argument is also called a higher-order function.

Example
def double(number):
    return number * number

def half(number):
    return number / 2

def get_number(function, number):
    result = function(number)
    return result

print(get_number(double, 2))
print(get_number(half, 2))
4
1

Here, we passed the half() and double() functions as input parameters to the get_number() function.


Return a Function

Python allows you to return a function from another function.

A function within a function we also called a nested function.

Example
def welcome():
    def greet():
        print("Welcome.")
    return greet

text = welcome()
text()

# text = welcome()()
# text
Welcome.

As you can see, we declared a welcome() function with an inner (nested) greet() function, and we returned the inner greet function from welcome() function.


Call the Decorator Function

Let's get back to decorator functions.

A decorator function adds functionality to an existing Python function.

The process is similar to packing a gift. Decorators serve as wrappers. It does not change the nature of the object (actual gift inside) that got decorated. However, it looks pretty now (since it has been decorated).

Example
def welcome(function):
    def greet():
        print("Welcome.")
        return function()
    return greet

def say_hello():
    print("Hello World.")

say_hello()

text = welcome(say_hello)
text()
Hello World.
Welcome.
Hello World.

In the above example, we called the say_hello() function the two ways. At first, we call the say_hello() function the regular way, and the second time we pass the say_hello() function to the welcome() function as an input parameter.

In the output, you can see that the first regular call only printed the text present in the say_hello() function, whereas the second call printed the text from both say_hello() and welcome() function.

The welcome() function (decorator) adds functionality (printing texts from decorator) to the say_hello() function without making any changes to the say_hello() function. That is why the welcome() function is considered a decorator function.

There are two ways to call the decorator function.

1. Passing the Python function as an input parameter to the decorator function.

def say_hello():
    print("Hello World.")

welcome(say_hello)
or

Call a decorator function with @ symbol, and declare it before the Python function declaration.

@welcome
def say_hello():
    print("Hello World.")

In above, both cases, syntax works similarly.


Syntax

The decorator functions are accessible by adding @ before the function name (@decorator_python_function).

def decorator_function(function):
    def wrapper_function():
        # Before calling the function, do something.
        function()
        # After calling the function, do something.
    return wrapper_function

# To call a decorator function, add '@' before its name.
@decorator_function
def python_function():
    pass

Python Custom Decorator Function

Example
def capital_text_decorator(function):
    def wrapper():
        text = function()
        return text.upper()
    return wrapper

@capital_text_decorator
def say_hello():
    return "Hello john"

print(say_hello())
HELLO JOHN

The capital_text_decorator() function was called and applied to the say_hello() function without changing its structure. The output shows that all characters of the string are in the uppercase, which was returned by the say_hello() function.


Python Decorator Function With Arguments

Additionally, decorators can accept arguments for Python functions, modify those arguments, and then pass those arguments to the Python function themselves.

Example
def capital_text_decorator(function):
    def wrapper(arg1):
        text = function(arg1.upper())
        return text
    return wrapper

@capital_text_decorator
def say_hello(name):
    return "Hello " + name

print(say_hello("john"))
Hello JOHN

As you can see in the output, the only input argument to the decorator function is converted into uppercase letters.


Python Function With Multiple Decorators

There is no restriction to calling multiple decorators on a single Python function.

Multiple decorators are called by the machine using the top-down approach.

Example
def capital_text_decorator(function):
    def wrapper():
        text = function()
        return text.upper()
    return wrapper

def welcome(function):
    def greet():
        print("Welcome.")
        return function()
    return greet

@welcome
@capital_text_decorator
def say_hello():
    return "Hello john"

print(say_hello())
Welcome.
HELLO JOHN

In the example, we called two decorators (welcome(), capital_text_decorator()) on a single Python function (say_hello()).