Python Decorator Fundamentals

PythonPythonBeginner
Practice Now

Introduction

Decorators in Python allow you to modify or enhance the behavior of a function, class, or method. They are a way to dynamically alter the functionality of your code without changing the source code. In this lab, we will explore the basics of decorators and how to use them to add functionality to your code.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("`Python`")) -.-> python/FunctionsGroup(["`Functions`"]) python(("`Python`")) -.-> python/DataStructuresGroup(["`Data Structures`"]) python(("`Python`")) -.-> python/ModulesandPackagesGroup(["`Modules and Packages`"]) python/FunctionsGroup -.-> python/keyword_arguments("`Keyword Arguments`") python/DataStructuresGroup -.-> python/tuples("`Tuples`") python/FunctionsGroup -.-> python/function_definition("`Function Definition`") python/ModulesandPackagesGroup -.-> python/importing_modules("`Importing Modules`") python/ModulesandPackagesGroup -.-> python/standard_libraries("`Common Standard Libraries`") python/FunctionsGroup -.-> python/build_in_functions("`Build-in Functions`") subgraph Lab Skills python/keyword_arguments -.-> lab-76{{"`Python Decorator Fundamentals`"}} python/tuples -.-> lab-76{{"`Python Decorator Fundamentals`"}} python/function_definition -.-> lab-76{{"`Python Decorator Fundamentals`"}} python/importing_modules -.-> lab-76{{"`Python Decorator Fundamentals`"}} python/standard_libraries -.-> lab-76{{"`Python Decorator Fundamentals`"}} python/build_in_functions -.-> lab-76{{"`Python Decorator Fundamentals`"}} end

Understanding Decorators

Let's start with a simple example. Open up a new Python interpreter session:

python3

We have a function that calculates the square of a number:

def square(x):
    return x**2

Now let's say we want to add a feature to the square function that logs the input and output of the function every time it is called. We can do this using a decorator.

Here's the code for the logger decorator:

def logger(func):
    def wrapper(x):
        print("Calling function:", func.__name__)
        print("Input:", x)
        result = func(x)
        print("Output:", result)
        return result
    return wrapper

Now we can use this decorator to enhance the square function:

@logger
def square(x):
    return x**2

When we call square(5), the output will be:

Calling function: square
Input: 5
Output: 25

As you can see, the decorator has added the desired functionality to the square function without changing its source code.

Decorators with Parameters

Decorators can also accept parameters. For example, we could modify the logger decorator to accept a message to be printed before the input and output logs.

def logger(message):
    def decorator(func):
        def wrapper(x):
            print(message)
            print("Calling function:", func.__name__)
            print("Input:", x)
            result = func(x)
            print("Output:", result)
            return result
        return wrapper
    return decorator

Now we can use this updated decorator to enhance the square function:

@logger("This is a custom message")
def square(x):
    return x**2

When we call square(5), the output will be:

This is a custom message
Calling function: square
Input: 5
Output: 25

Using Multiple Decorators

You can also stack multiple decorators on top of each other to add multiple levels of functionality. For example, let's create a new decorator called timer that measures the time it takes for a function to run:

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print("Time elapsed:", end_time - start_time)
        return result
    return wrapper

Now let's use both the logger and timer decorators on the square function:

@logger("This is a custom message")
@timer
def square(x):
    return x**2

When we call square(5), the output will be:

This is a custom message
Calling function: square
Input: 5
Output: 25
Time elapsed: 0.000001

As you can see, the timer decorator has added the time elapsed information to the square function, while the logger decorator has added the input and output logs and the custom message.

Summary

In this lab, we have learned the basics of decorators in Python. We have seen how to use decorators to modify the behavior of a function, class, or method, and how to use multiple decorators to add multiple levels of functionality. We have also seen how to use decorators with parameters to add custom functionality.

With these skills, you can now start incorporating decorators into your code to make your life as a developer easier!