Before starting about decorators, first, understand that functions in python have below three properties.
1. Functions are objects. Which means we can assign the function to a variable.
2. A function can be defined inside another function
3. A function can return another function
See example below:
Assigning a function to another variable
# Assigning function to another variable # a function which adds * around text def beautify(txt="python"): print("*" + txt + "*") # Assign above function to another variable star = beautify # call star print(star()) # output >>> *python*
Defining a function inside another function
def beautify(): # define an inner function def inside_beauty(txt="python"): print("*" + txt + "*") # call inner function inside_beauty() # call outer function beautify() # output >>> *python*
Returning function from another function
def beautify(): # define an inner function def inner_beauty(txt="python"): print("*" + txt + "*") # return this inner function return inner_beauty # collect return type of beautify in a variable f = beautify() print(f) # output >>> <function inner_beauty at 0x7fe5c176b7d0>
# call f f() # output >>> *python*
Now when we know that we can assign a function to a variable, can define a function inside another function and can return a function from another function, we can jump to next strep. We can pass a function as a parameter to another function.
def beautify(func): print("going to call a function passed as parameter to this function") func() print("function which was passed as parameter has been called")
def add_stars(txt="python"): print("*" + txt + "*") beautify(add_stars) # output >>> going to call a function passed as parameter to this function *python* function which was passed as parameter has been called
Now coming to what a decorator is: Decorator is a wrapper function that executes some code before and after the function being decorated, just like the example above.
Another Example:
Let's say you want to separate the output of a function by surrounding it with horizontal dividers.
Original Function:
def say_something(): print("I am the original function.")
Decorator function:
def separate_content(function_to_decorate): # a wrapper function def wrapper_func(): print("---------------------------") function_to_decorate() print("---------------------------") return wrapper_func
Now if you call the original function, the output will be I am the original function.
You can decorate the original function by passing it to the decorator function as below.
say_something = separate_content(say_something) say_something() # output --------------------------- I am the original function. ---------------------------
Shortcut to decorate a function is:
@separate_content def say_something(): print("I am the original function.")
which when called will give the same out as above.
Now you can just use @separate_content
above any function definition and the behavior of that method will be changed without changing the content of that method