First Class Functions, Closures, and Decorators in Python

Navin Poudel
4 min readSep 8, 2019

What programming paradigm does Python language follow? To be quick and simple, Python supports multi-paradigm including Procedural, Object-Oriented and Functional.

So, before even starting decorators or anything else, let’s give a quick look towards the Functional Paradigm in Python. Any language is quoted as “functional” if functions are its first-class citizens. What does that mean? It includes three things:

a) Functions can be stored in a variable and can be executed with the variable name from anywhere in the program.

b) A Function can be passed to another function as an argument.

c) A Function can be returned from a function.

Let’s look at a code snippet below:

def addition(a,b):
return a+b
add = addition
def summation(x, y):
return add(x, y)
def do_sum(a, b, func):
return func(a,b)
print(add(2,3)) # using variable name to call the fxn.
print(summation(5,3)) # calling a function to return another fxn.
print(do_sum(1,3,summation)) # passing fxn to another fxn.

So, on the execution of the above code gives three integers 5, 8 and 4. This satisfied all the three points mentioned above. Now, this brings the concept of closures in python. Closures can be explained as the scope of the parameter of the outer function to its inner function. For example:

def outer_function(msg):
def inner_function():
print(msg)
return inner_function
message = outer_function("Python is a wonderful language")
message()

Here outer_function() takes a message as an argument and its scope also lasts inside the inner_function which prints the message when called. The outer_function() returns inner_function ready to be executed. So, finally when the message() is executed then it prints the message passed as the argument to the outer_function(). Long story short, we executed inner_function() by calling the outer_function() which returned a function in the message variable.

Now the concept of the first-class function and closures bring us to the Decorators. Decorators simply are the function that takes our main function as an argument and returns a function by adding functionality to our main function but not altering any functionality of our main function. Let’s understand this with code.

def decorator_function(main_function):
def wrapper_function():
print("I am a passionate Software Engineer")
return main_function()
return wrapper_function
def display():
print("Hello, what's up friends?")
msg = decorator_function(display)
msg()

In the above example, we have got our main function display() which simply prints a message. Now, this function is passed as an argument to a decorator_function(). Inside the decorator_function() we have got a wrapper_function() which prints another message and returns our main_function. The decorator_function() returns the wrapper_function ready to be executed. So, it is returned to a variable “msg”. Now, when msg() is called, it does the task defined inside our main function display() i.e. it prints “Hello, what’s up friends” but also adds another functionality by printing “I am a passionate Software Engineer.” It makes more sense if we replace our variable “msg” with “display”.

def decorator_function(main_function):
def wrapper_function():
print("I am a passionate Software Engineer")
return main_function()
return wrapper_function
def display():
print("Hello, what's up friends?")
display = decorator_function(display)
display()

Here we can see the magic happening. We simply called the display() function to print the message inside it but we ended up printing both the messages. Isn’t it amazing? Finally, that one line “display =decorator_function(display)” is equivalent to the @decorator_function above our display().

def decorator_function(main_function):
def wrapper_function():
print("I am a passionate Software Engineer")
return main_function()
return wrapper_function
@decorator_function
def display():
print("Hello, what's up friends?")
display()

But, the above code is limited to “no use arguments” inside our main function. That means if we pass arguments to the display() function, perform some calculations and return the result then it throws an error from the wrapper_function saying “wrapper_function takes zero positional arguments, but some were given”. No worries, we can solve this limitation in a pythonic way. Yes, we can make use of *args and **kwargs. *args and **kwargs allow you to pass a variable number of arguments to a function. So, we can pass them to the wrapper_function() and the main_function() inside it so that we can pass any number of arguments to the display() function, perform the operations defined inside it and still be able to make the use of decorator. So, our final code looks like below:

def decorator_function(main_function):
def wrapper_function(*args, **kwargs):
print("I am a passionate Software Engineer")
return main_function(*args, **kwargs)
return wrapper_function
@decorator_function
def display(msg):
print(msg)
display("Hello, what's up friends?")

So, this was a little information on the fundamentals of decorators in python. Now, let’s make our own custom decorators, fit it with our functions and solve the problems in a smarter and more pythonic way.

--

--