Python Decorators
Python Decorators
This page covers Python Decorators.
Similar to the idea of a gift decoration, Python decorators wrap functions.
The concept of decorators wraps one function over another. In other words, it transform functions by adding more functionality.
This method was designed to replace the older transformative approach, which is less efficient.
Before exploring decorators, let’s understand functions better.
First-class objects
In Python, functions are first-class objects.
If you are not familiar with Python functions, please check here first.
First-class functions refer to functions that can be:
- stored in variables
- defined in other functions (nested)
- passed as arguments
- returned by other functions
Let’s briefly explore the above first-class function characteristics.
Normal functions
def func(text): print(text) func("Hello, World!") # Output: Hello, World!
Function stored in a variable
def func(text): print(text) someVariable = func someVariable("Hello, World!") # Output: Hello, World!
A function defined in another function (nested functions)
def func(text): print("Start") def wrapper(text): print(text) print("End") wrapper(text) func("Hello, World!") # Start # Hello, World! # End
Function passed as an argument
def func(): print("Hello, world!") def another_func(func): func() another_func(func) # Output: Hello, world!
A function returned by another function
def func(): print("Hello world, from the function.") def inside_func(): print("Hello world, from the inner function.") return inside_func return inside_func() func() # Hello world, from the function. # Hello world, from the inner function.
The above is a concise explanation of the characteristics of a first-class function. Now, we can dive into the concept of decorators.
Decorators
The operation behind decorators is transforming functions.
Applying a decorator over a function allows a redesign of that function. As such, new functionalities can be added.
To apply a decorator, use the “@” symbol.
Let’s create a simple decorator, which can be used at any time later on. The decorator adds the message “Adding more..”.
def decorator(func): def wrapper(): print("Adding more..") func() return wrapper
Now, we create a simple function.
def some_text(): print("Hello, world!") some_text() # Output: Hello, world!
The function is very basic and has one functionality, to print “Hello, world!”. We can now add the functionality of the decorator.
Let’s apply the decorator (with @) and call the function again. Decorators always go above the function.
@decorator def some_text(): print("Hello, world!") some_text() # Adding more.. # Hello, world!
We can apply the same decorator to multiple functions as well as multiple decorators to one function.
Below is an example. We create a second function, displaying the length of a list of numbers. In addition, we create a second decorator (decorator_2) and apply both decorators to both functions.
def decorator(func): def wrapper(): print("Adding more..") func() return wrapper def decorator_2(func): def wrapper(): print("Adding even more..") func() return wrapper @decorator @decorator_2 def some_text(): print("Hello, world!") @decorator @decorator_2 def some_numbers(): numbers = [2, 7, 12, 1, 7] print(len(numbers)) some_text() some_numbers()
Let’s check the outcome.
Adding more.. Adding even more.. Hello, world! Adding more.. Adding even more.. 5
Decorators with arguments (*args & **kwargs)
We can also pass arguments through decorators.
This can happen through the use arbitrary arguments (*args) and keyword arbitrary arguments (**kwargs).
For more here.
Let’s create an example of a decorator with some arguments.
def decorator(func): def wrapper(*args, **kwargs): print("Adding", args) print("Adding", kwargs) func() return wrapper @decorator def some_text(): print("Hello, world!") some_text(1, 2, 3, first=1, second=2)
Let’s see the output.
Adding (1, 2, 3) Adding {'first': 1, 'second': 2} Hello, world!