In Python, decorators are functions that add functionality to existing Python functions without changing the structure of the Python function itself.
Before going to the next, we understand Python some basic concepts.
Python treats everything as an object, including classes and functions. Names are simple identifiers for those objects. Different names can be assigned to the same function object.
def welcome(name):
print(name)
welcome("John")
hello = welcome
hello("John")
'John'
'John'
The output of both functions welcome()
and hello()
is the same. The welcome()
and hello()
refer to the same function object.
It is possible to pass functions as arguments to other functions.
A function that takes another function as an argument is also called a higher-order function.
def double(number):
return number * number
def half(number):
return number / 2
def get_number(function, number):
result = function(number)
return result
print(get_number(double, 2))
print(get_number(half, 2))
4
1
Here, we passed the half()
and double()
functions as input parameters to the get_number()
function.
Python allows you to return a function from another function.
A function within a function we also called a nested function.
def welcome():
def greet():
print("Welcome.")
return greet
text = welcome()
text()
# text = welcome()()
# text
Welcome.
As you can see, we declared a welcome()
function with an inner (nested) greet()
function, and we returned the inner greet
function from welcome()
function.
Let's get back to decorator functions.
A decorator function adds functionality to an existing Python function.
The process is similar to packing a gift. Decorators serve as wrappers. It does not change the nature of the object (actual gift inside) that got decorated. However, it looks pretty now (since it has been decorated).
def welcome(function):
def greet():
print("Welcome.")
return function()
return greet
def say_hello():
print("Hello World.")
say_hello()
text = welcome(say_hello)
text()
Hello World.
Welcome.
Hello World.
In the above example, we called the say_hello()
function the two ways.
At first, we call the say_hello()
function the regular way, and the second time we pass the say_hello()
function to the welcome()
function as an input parameter.
In the output, you can see that the first regular call only printed the text present in the say_hello()
function, whereas the second call printed the text from both say_hello()
and welcome()
function.
The welcome()
function (decorator) adds functionality (printing texts from decorator) to the say_hello()
function without making any changes to the say_hello()
function. That is why the welcome()
function is considered a decorator function.
1. Passing the Python function as an input parameter to the decorator function.
def say_hello():
print("Hello World.")
welcome(say_hello)
Call a decorator function with @
symbol, and declare it before the Python function declaration.
@welcome
def say_hello():
print("Hello World.")
In above, both cases, syntax works similarly.
The decorator functions are accessible by adding @
before the function name (@decorator_python_function
).
def decorator_function(function):
def wrapper_function():
# Before calling the function, do something.
function()
# After calling the function, do something.
return wrapper_function
# To call a decorator function, add '@' before its name.
@decorator_function
def python_function():
pass
def capital_text_decorator(function):
def wrapper():
text = function()
return text.upper()
return wrapper
@capital_text_decorator
def say_hello():
return "Hello john"
print(say_hello())
HELLO JOHN
The capital_text_decorator()
function was called and applied to the say_hello()
function without changing its structure. The output shows that all characters of the string are in the uppercase, which was returned by the say_hello()
function.
Additionally, decorators can accept arguments for Python functions, modify those arguments, and then pass those arguments to the Python function themselves.
def capital_text_decorator(function):
def wrapper(arg1):
text = function(arg1.upper())
return text
return wrapper
@capital_text_decorator
def say_hello(name):
return "Hello " + name
print(say_hello("john"))
Hello JOHN
As you can see in the output, the only input argument to the decorator function is converted into uppercase letters.
There is no restriction to calling multiple decorators on a single Python function.
Multiple decorators are called by the machine using the top-down approach.
def capital_text_decorator(function):
def wrapper():
text = function()
return text.upper()
return wrapper
def welcome(function):
def greet():
print("Welcome.")
return function()
return greet
@welcome
@capital_text_decorator
def say_hello():
return "Hello john"
print(say_hello())
Welcome.
HELLO JOHN
In the example, we called two decorators (welcome()
, capital_text_decorator()
) on a single Python function (say_hello()
).