@decorators in Python

Rupesh Mishra
4 min readMay 11, 2017

--

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.

Decorators

Decorators are callable objects which are used to modify functions or classes.

Callable objects are objects which accepts some arguments and returns some objects. functions and classes are examples of callable objects in Python.

Function decorators are functions which accepts function references as arguments and adds a wrapper around them and returns the function with the wrapper as a new function.

Let’s see this with an example:

In the above example, decorator_func is the decorator function which accepts some_func, a function object, as an argument. It defines a wrapper_func which calls some_func but it also executes some code of its own.

This wrapper_func is returned by our decorator function and it’s stored in say_hello variable. Thus, now say_hello refers to wrapper_func which has some extra code apart from calling the function which was passed as an argument. Thus in another way, we can say that decorator function modified our say_hello function and added some extra lines of code in it. This is what decorators are. The output is the modified say_hello function with extra print statements

Python syntax for the decorator

@decorator_func
def say_hell():
print 'Hello'

The above statements are equivalent to

def say_hello():
print 'Hello'
say_hello = deocrator_func(say_hello)

Here, decorator_func will add some code on top of the say_hello function and will return the modified function or the wrapper function.

Functions with arguments and decorators

Consider the below example for function with parameters and decorators:

Here, we have defined say_hello function with two arguments and @decorator_func. The inner function of decorator_func i.e. wrapper_func must take exactly the same number of argument as say_hello function.

Here, @decorators_func validates if the passed parameters are not empty and none, if they are, it calls say_hello function with default arguments.

Passing parameter to a decorator function

To pass the parameter to a decorator function we write a wrapper function to the decorator which then defines the decorator function inside itself. Example:

--

--