Python From Beginner to Advanced

0% completed

Previous
Next
Python - Decorators

In this lesson, we explore the functionality of decorators in Python, starting with understanding how functions in Python can be treated as first-class objects. We will demonstrate this with simple examples and then explore why decorators are essential for enhancing the functionality of functions without altering their core definition.

First-Class Objects

Functions in Python are first-class objects, meaning they can be passed around and used as arguments just like any other object.

Example: Assigning Functions to Variables

This example shows how to assign a function to a variable:

Python3
Python3

. . . .

Explanation:

  • def greet(): defines a simple function that prints a greeting.
  • greet_func = greet assigns the greet function to the variable greet_func.
  • greet_func() calls the function using the new variable name, demonstrating that the function behaves like an object.

Example: Passing Functions as Arguments

Functions can be passed as arguments to other functions:

Python3
Python3

. . . .

Explanation:

  • shout and whisper functions change the case of the text.
  • explain function takes another function and a string, applies the function to the string, and prints the result.
  • explain(shout, "Hello") and explain(whisper, "Hello") demonstrate dynamic function passing.

Why We Need Decorators

Decorators provide a way to modify functions using other functions without changing the function's source code. This capability is particularly useful for adding features like logging, access control, memoization, and more.

Example: A Simple Logging Decorator

Here’s a basic decorator that adds logging functionality to any function it decorates:

Python3
Python3

. . . .

Explanation:

  • def logger(func): The logger function defines the decorator accepting func, the function to be decorated.
  • Inside logger, wrapper is defined which encapsulates the added functionality. Before calling the func, it logs "Logging execution...", then calls func(), and logs "Done logging." after the function executes.
  • @logger is used to apply the decorator to the sample function. When sample() is called, it first executes the logging statements, then the original function body.

Example: Decorator with Parameters

This example shows a decorator that accepts parameters, allowing for more dynamic behavior:

Python3
Python3

. . . .

Explanation:

  • def repeat(times): This is a decorator factory that takes times as an argument, which specifies how many times the decorated function should be repeated.
  • decorator_repeat(func): Defines the actual decorator that wraps the function func.
  • wrapper(*args, **kwargs): The wrapper function that repeats the function call as many times as specified by times. It accepts any number of positional and keyword arguments to pass to func.
  • Each iteration logs the function name and iteration number before calling func.

Handling Return Values and Arguments

When a function that returns values or takes arguments is decorated, the decorator must be properly designed to handle these aspects.

Example: Decorator Handling Arguments and Return Values

Here is a decorator that performs type checking for function arguments:

Python3
Python3

. . . .

Explanation:

  • type_check is applied to sum_numbers. It ensures that every argument passed to sum_numbers is an integer.
  • If any argument is not an integer, it raises a ValueError.
  • If all arguments are integers, it calls the function and returns the result.

Chaining Decorators

Applying multiple decorators to a function is known as chaining. Each decorator adds its layer of functionality in the order specified.

Example: Chaining Logging and Type Checking Decorators

Here, both logging and type checking functionalities are added to a function:

Python3
Python3

. . . .

Explanation:

  • @type_check ensures all arguments are integers and then @logger logs the execution.
  • The function multiplies the input numbers if all checks pass.

Use Case: Web Application Routing

Decorators are extensively used in web frameworks to map URLs to functions that generate web pages:

Example

Python3
Python3

. . . .

Explanation:

  1. Initialization: An empty dictionary named routes is created. This dictionary will be used to store URL paths as keys and corresponding functions as values.

  2. Decorator Definition: A function named route is defined. This function takes a path as an argument and returns a decorator function.

  3. Decorator Usage: The route decorator is used to associate specific URL paths with functions. For example, @route("/home") associates the /home URL path with the home_page function, and @route("/about") associates the /about URL path with the about_page function.

  4. Function Definition: Functions like home_page and about_page are defined. These functions represent the content of different pages of a hypothetical website.

  5. Request Simulation: The simulate_request function is defined to simulate a web request to a specified path. It takes a path as an argument and prints the result of executing the function associated with that path.

  6. Execution: The simulate_request function is called twice, once with the /home path and once with the /about path. This simulates visiting the home page and the about page of the simulated website, respectively.

  7. Output: The content of the requested pages is printed to the console.

Decorators thus serve as powerful tools for enhancing and organizing Python code, especially in complex applications where modularity, reusability, and cleanliness are crucial.

.....

.....

.....

Like the course? Get enrolled and start learning!
Previous
Next