Photo by Fatos Bytyqi on Unsplash

In this article, we will have a detailed discussion on function and class decorators in Python. We will begin with understanding functions in Python and later with the help of examples, we will understand the internal working of decorators.

Functions in Python

In Python, everything is an object. Not only complex datatypes like list or dict but even simple data types like int , float , etc are objects in Python. Functions in Python are also objects. They are considered as the first-class object.

First-class objects in a programming language are entities that behave just like normal objects. They can be referenced by variables, stored in data structures like list or dict, passed as arguments to another function, returned as a value from another function.

Functions referenced by another variable

Consider a simple example to demonstrate that functions can be referenced by variables. say_hello is a simple function which prints Hello .

In the code below, we have assigned say_hello to another variable called say_hello2 . After the assignment, but say_hello and say_hello2 are pointing to the same function. Hence, calling say_hello2 will also print Hello just like say_hello . We can also verify that both say_hello and say_hello2 are pointing to the same location.

Function passed as an argument to another function

Consider the example below. We have defined two functions say_hi and say_hello . say_hello accepts one argument say_hi_func . As shown on line 9, we have passed say_hi function as an argument to say_hello function. say_hello function calls say_hi function inside its body. say_hi function and say_hi_func are both pointing to the same function. Hence, when say_hi_func is called it prints Hi from say_hi function

Function defined inside another function

In the below code snippet, we have defined two functions parent_function and child_function . When we call the parent_function , child_function will be created inside the parent_function . child_function will be accessible only inside the parent_function . Once the parent_function execution is complete child_function will get destroyed by the Python. An attempt to access the child_function outside the parent_function will raise an exception.

Function returning another function

In the below code snippet, we have defined a function called parent_function that defines another function inside its definition called child_function . This is same as the previous example. But, in this example instead of calling the child_function , parent_function returns the child_function . Returned child_function is assigned to the variable child_function_another_ref . Now, both child_function and child_function_another_ref are pointing to the same function definition.

Unlike the previous example, when child_function was not accessible outside the parent_function definition, here child_function_another_ref will refer to the child_function definition. Hence calling child_function_another_ref will not raise an exception. But, calling child_function outside the parent_function will raise an exception as variable child_function will not be available outside the parent_function .

Another example with function arguments

Variable hello_var is accessed even outside the say_hello function because say_hi function was defined inside say_hello function so it can access all the variables of say_hello function. This is called closure.

With the understanding of functions, now let’s understand decorators.