Step-by-Step Guide to Python Decorators: Learn with Clear and Simple Examples

As a Python developer and AI researcher, I’ve found that mastering decorators can significantly enhance your coding efficiency and style. In this article, I’ll walk you through the concept of Python decorators, explain their usefulness, and provide clear examples to illustrate their power.

What are Python Decorators?

Python decorators are a powerful and elegant way to modify or enhance functions or methods without changing their source code. They allow you to wrap a function, adding new functionality to an existing function with minimal modification.

Why Use Decorators?

Decorators offer several benefits:

  1. Code reusability
  2. Clean and maintainable code
  3. Separation of concerns
  4. Easy to add or remove functionality

Understanding Decorators: A Simple Example

Let’s start with a basic example to understand how decorators work. We’ll create a decorator that measures the execution time of a function.

Without Decorator

First, let’s look at how we might measure execution time without using a decorator:

import requests
import time

print(‘—————-“without decorator”—————————‘)

def get_req(url):
    r = requests.get(url)
    if r.status_code == 200:
        print(r)
        return r
    else:
        return ‘failed to fetch data’

start = time.time()
get_req(‘https://example.com/’)
end = time.time()
print(f’Execution time for “get_req” function is : {end – start} seconds’)

print(‘================================================’)

def say_hello():
    print(‘hello world’)
    time.sleep(1)

start = time.time()
say_hello()
end = time.time()
print(f’Execution time for “say_hello” function is : {end – start} seconds’)

In this example, we’re manually timing each function call. This approach works, but it’s repetitive and clutters our main code.

With Decorator

Now, let’s see how we can achieve the same result using a decorator:

import time
import requests

print(‘—————-“with decorator”———————–‘)  

def afra(f):
    def wrapper(*args, **kwargs):
        start = time.time()
        x = f(*args, **kwargs)
        end = time.time()
        print(f’Execution time for {f} is : {end – start} seconds’)
        return x
    return wrapper

@afra
def get_req(url):
    r = requests.get(url)
    if r.status_code == 200:
        print(r)
        return r
    else:
        return ‘failed to fetch data’

get_req(‘https://example.com/’)

print(‘==================================================’)

@afra
def say_hello():
    print(‘hello world’)
    time.sleep(1)

say_hello()

Breaking Down the Decorator

Let’s analyze our “afra” decorator:

  1. We define a function afra that takes another function f as an argument.
  2. Inside afra, we define a wrapper function that:
    • Records the start time
    • Calls the original function f
    • Records the end time
    • Prints the execution time
    • Returns the result of the original function
  3. The afra function returns the wrapper function.

When we use the @afra syntax above a function definition, Python automatically wraps that function with our afra decorator.

The Power of Decorators

Notice how clean our code becomes with decorators. We no longer need to manually time each function call. Instead, we simply add @afra above any function we want to time.

This demonstrates the key advantages of decorators:

  1. Reusability: We can use @afra on any function we want to time.
  2. Clean Code: Our main code is no longer cluttered with timing logic.
  3. Separation of Concerns: The timing functionality is separate from our main function logic.
  4. Flexibility: We can easily add or remove the timing functionality by adding or removing the decorator.

Conclusion

Python decorators are a powerful tool that can significantly improve your code’s readability, maintainability, and reusability. By understanding and utilizing decorators, you can write more elegant and efficient Python code.

Remember, the example we’ve explored here is just the tip of the iceberg. Decorators can be used for a wide range of purposes, from logging and timing to authentication and caching.

Leave a Reply