Decorators: end you everlasting confusion about decorators and its uses
Whenever going through github or any other codebase, we often come across somthing that looks unlike python.
@decorator
def my_function():
pass
This is known as a decorator in python and is extremely handy and powerful. A decorator is used when we need to change the behaviour of a function without changing the function itself. Makes sense? It will now.
It can be said that almost everything in python is an object. Functions are also objects(first class). Therefore we can assign functions to a variable.
def my_function():
print("hello dev community")
#we can assign the function without executing to the variable func
func=my_function
Functions are first class objects
They can be assigned to a varibale as shown earlier.
Moreover they can be passed as argument to another function and also nested into another function.
def outer(message):
def inner():
print("hi "+ message)
return inner
hi = outer("dev community")
bye = outer("dev community")
#this executes the inner function.
hi()
bye()
output
hi dev community
bye dev community
Here we have two nested function outer
and inner
outer
returns inner
without executing it. When we assign outer
to a variable and execute the variable,it actually executes inner
with the specific argument.
This is called a closure. Closures can avoid the use of global values and provides some form of data hiding. It can also provide an object oriented solution to the problem.
Decorators extensively use closures
Rather than taking message
as an argument decorators take functions as arguments.Let's create a decorator step-by-step.
def decorator_function(original_function):
def wrapper_function():
#do something before the function
print("hello from wrapper")
return original_function()
return wrapper_function
def display():
print("hello from display")
decor_display = decorator_function(display)
decor_display()
The code looks extremly similar to the closure we created earlier. We are now passing a function through decorated_function
.
When we assign decor_display
the decorator_function
executes and returns wrapper_function
. We then execute the wrapper_function
stored in decor_display
by invoking decor_display
in the last line. Now the wrapper_function
executes its print statement and the original_function
which in our context is display
and finally outputs:
hello from wrapper
hello from display
That was a lot to take in for sure.Go through the process again and hopefully the logic will be clear.
Here we successfully managed to changed the behaviour of the display
function by printing and extra line before it hello from wrapper
. We did not change the display
function itself but rather changed its behaviour using our decorator_function
.
Python allows us to do this same thing using @decorator_function_name
. Rather than assigning decorator_function
to a variable we can assign it to any function using @decorator_function
before the function.
def decorator_function(original_function):
def wrapper_function():
#do something before the function
print("hello from wrapper")
return original_function()
return wrapper_function
#this works same as decorator_function(display)
@decorator_function
def display():
print("hello from display")
display()
output
hello from wrapper
hello from display
Conclusions
Decorators are a powerful tool and is used in many codebases for various purposes such as logging, performance testing, perform caching, verifying permissions etc. If you find this tutorial useful do comment below. Thank you for reading and learning.
Top comments (0)