Chapter 12: Decorators and Context Managers

Decorators and context managers are advanced features in Python that enhance code functionality and manage resources effectively. Decorators allow you to modify or extend the behavior of functions or methods, while context managers help manage resources, such as file handling, with clean and concise syntax.

Decorators

A decorator is a function that takes another function as input and extends or modifies its behavior without changing its structure.

Anatomy of a Decorator:

  1. Define a decorator function.

  2. Accept a function as an argument.

  3. Define a wrapper function inside the decorator.

  4. Return the wrapper function.

Example:

def my_decorator(func):
    def wrapper():
        print("Before the function call")
        func()
        print("After the function call")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()
# Output:
# Before the function call
# Hello!
# After the function call

Using Decorators with Arguments:

Built-in Decorators

Python provides several built-in decorators such as @staticmethod, @classmethod, and @property.

Example:

Context Managers

Context managers handle resource management tasks such as opening and closing files, acquiring and releasing locks, etc. The most common example is the with statement.

Example with with:

Creating Custom Context Managers

Custom context managers can be created using the __enter__() and __exit__() methods in a class.

Example:

Contextlib Module

The contextlib module provides utilities for creating and working with context managers.

Example with contextlib:

Combining Decorators and Context Managers

You can use both features together to create clean, efficient, and readable code.

Example:

Exercises

Exercise 1:

Write a decorator that logs the execution time of a function.

Solution:

Exercise 2:

Create a context manager that suppresses specific exceptions.

Solution:

Exercise 3:

Create a custom context manager to log the entry and exit of a block.

Solution:

Best Practices

  1. Use decorators for reusable and modular functionality.

  2. Prefer contextlib for simple context managers.

  3. Test custom context managers and decorators for edge cases.

  4. Keep wrapper functions in decorators lightweight.

  5. Use meaningful names for decorators and context manager classes.

In the next chapter, we will explore concurrency and parallelism, focusing on multithreading, multiprocessing, and asynchronous programming in Python.

Last updated