From logging to memorization, unlock the power of this versatile feature for cleaner and more efficient code. Learn more here.
Python offers a feature known as a decorator, which is a design pattern that allows for the extension or modification of a function's behavior by wrapping it with another function. This allows you to add, change, or extend the functionality of the original function without modifying its original source code.
Explore this article to learn more about decorators in Python, including types of decorators, how to use them, some use cases for decorators, and how to get started.
A decorator in Python is a function that allows programmers to modify the behaviors of functions as input and return a new function with the modified behavior without changing the original code. Decorators can also extend the behavior of other objects other than functions, such as classes or methods. When writing code to decorate a function with a class, use the @ syntax followed by the class name you want to decorate, then add the function definition below it.
In Python, functions are first-class objects. As a result, functions can be used as arguments. A decorator is a higher-order function, which means that it accepts other functions as arguments and returns a new, enhanced function, method, or class.
When decorators modify a function’s behavior, they can do several things. Here are some capabilities of a decorator in Python explained:
Nested functions refer to the concept of defining a function within another function. In the context of decorators, the nested function is the decorator function. Another term for the nested function is the inner function. Put simply, a nested function is a function that is defined inside of another function, which allows you to decorate a function multiple times.
To return a function as a value means that the function acts as the return value from another function, which allows functions to pass as arguments to other functions, returned as values from functions, or assigned to variables.
An argument is a value that is accepted by a function. In Python, functions are first-class objects, meaning they can receive or pass as arguments. In other words, you can pass functions as arguments to other functions. Decorators use the passed function to enhance or modify its behavior. The inner function within the decorator (wrapper) usually invokes the original function and incorporates the added functionality.
In Python, several different types of decorators allow you to modify and extend the behavior of objects, such as functions, classes, or methods, in various ways and for different purposes.
Function decorators modify the behavior of a function by wrapping it with another function. You can also use function decorators as chaining decorators, assigning multiple decorators to a function.
A class decorator modifies or extends the behavior of a class by taking a class as its argument and returning a new class. Class decorators modify the behavior of a class by creating additional functionality, where the function of a class depends on whether or not an argument is accepted.
Class method decorators are specifically designed to add functions to a class—or modify the behavior of methods within a class. These decorators receive the method as an argument, modify it, and then return a new object.
Decorator chaining means applying multiple decorators to a single function, class, or method by stacking them on top of one another. The order in which you stack the decorators isn’t important, as regardless of the order, your output will ultimately produce the same results.
You can pass arguments to your decorators so that your decorator can take on more arguments, allowing for more flexibility. The outermost function takes the argument you want to pass to the decorator, which becomes the actual decorator. You define the decorator function to define the behavior of the original function, and then inside the decorator function, define a wrapper function that will call the original function with your defined arguments. Apply the decorator syntax followed by the name of the outermost function.
It’s helpful to break down the steps involved in creating and applying decorators to understand them more fully. To begin, write the function identifier or the name of your function, followed by a parenthesis and a colon. Below this line of code, indent with a tab and specify what you want the function to do.
After creating a function, you want to “call” the function when you want to use it. To make the function callable, write the name of the function followed by parentheses. When you call the function, you execute or return the codes inside of that function.
To create a decorator in Python, you first need a higher-order function that will function as an input and contain a nested function, or input function, that gives the input function the ability to modify its behavior. From there, you can call the function within the nested function and choose how to change its behavior by giving it additional functions before the higher-order function returns the nested function.
When you want to use the decorator, you will put an @ symbol, followed by the decorator’s name above the function you want to modify. For example, the syntax for a decorator in Python starts with the keyword ‘def’ to define your function, followed by the @ symbol and then the decorator's name. You can then add any arguments (the value sent to the function when called) you need to pass your function as an argument.
Because a decorator implements the _call_() syntax that allows you to take in another function and then return it by adding some additional functionality, it is “callable.”
Decorators find widespread application in various scenarios, enhancing code readability, modularity, and reusability. Some common use cases for decorators in Python include:
Logging is a fundamental aspect of software development for tracking the execution flow and diagnosing issues. In Python, you can use decorators to create logging wrappers that automatically log details about function calls. In this use case, decorators can simplify the addition of logging functionality across multiple functions.
Timing refers to measuring the execution time of functions. Called the timer function or timer decorator, you can use decorators to easily measure how much time a particular function takes to run. The timer decorator adds timing functionality to any function it decorates as it records the start time before the function is called, records the end time after the function execution, calculates the elapsed time, and then prints it. When you use this application of decorators, it will allow your function to take in the arguments and keywords of the original function and record time per parameters you set, depending on what values you use.
Caching is a technique used in computing to store and quickly retrieve data or computations that have already been processed or fetched, with the goal of improving performance and efficiency by reducing the need to repeat time-consuming operations. You can use decorators in Python for caching to store and reuse the results of expensive or time-intensive function calls, as the decorator adds caching functionality without modifying the original function. You can cache a function or method by decorating a function with the cache decorator.
Authorization ensures that certain functions are only accessible to authorized users, which is a requirement in many applications. You can use decorators to implement an authorization check before executing a function. In this use case, a decorator in Python example is the decorator taking a user type as an argument and returning a decorator. The decorator, in turn, checks if the user has the appropriate permissions before executing the function. The process ensures that certain operations are only accessible to authorized users.
Memoization is a technique where the results of expensive function calls are cached and reused when the same inputs occur again in order to speed up the execution of functions.
By applying the memoize decorator, you've added a memory mechanism to the function. The decorator keeps track of the function's previous results and reuses them if the same inputs occur again, avoiding redundant calculations. Memoization is particularly useful for functions with expensive computations, making your program more efficient.
If you’re ready to learn more about Python and how to create code, consider enrolling in an online course. The courses Crash Course on Python and Python for Everybody Specialization, both offered on Coursera, are excellent choices for someone who is just getting started in Python. Enroll today.
Github. "peps/peps/pep-0318.rst, https://github.com/python/peps/blob/main/peps/pep-0318.rst." Accessed March 22, 2024.
Editorial Team
Coursera’s editorial team is comprised of highly experienced professional editors, writers, and fact...
This content has been made available for informational purposes only. Learners are advised to conduct additional research to ensure that courses and other credentials pursued meet their personal, professional, and financial goals.