Decorated Python

- 4 mins

Closures

In Python we treat all the functions as objects.

What is so cool about it?

We can pass or return them to another functions to build new functions.

¯\_(ツ)_/¯

Consider the following function. Unlike normal functions we see, this is an example of function inside a function. The working is pretty straight-forward.

def outer_func():
    message = "Hello World!"
    def inner_func():
        print(message)
    return inner_func()
outer_func()

Let’s call this function.

--> Hello World!

We see that message is not a global variable accesible to all the functions defined, rather message is the local variable of outer_func. Though message is not defined inside inner_func, it is accesible to the inner function. That’s why these variables are calleds free variables. Now I’m going to make a little difference in the code, which is not calling the inner_func.

def outer_func():
    message = "Hello World!"
    def inner_func():
        print(message)
    return inner_func
outer_func()
--> <function __main__.outer_func.<locals>.inner_func>

Aaha! It is not printing Hello World! anymore, rather returning another a function as output. So how to print Hello World! then?

sample = outer_func()
sample()
--> Hello World!

WOO-HOO !!! So it works. Yet, we didn’t pass any parameters to the functions. Let’s try that now.

def outer_func(msg):
    message = msg
    def inner_func():
        print(message)
    return inner_func

hi_func = outer_func("Hi")
hello_func = outer_func("Hello")
hi_func()
hello_func()
--> Hi
--> Hello

So if you’ve noticed, each functions hi_func and hello_func maintained their value of free variables . i.e. Hi and Hello didn’t mixed up. It is because of the reason that the enviroments didn’t mixed up. The next obvious question is, what is an environment of a function.

What is an environment?

We all know about Global and Local variable scopes. An environment of a function includes the function itself and it’s possible variables in its scope. Here in this setup, Python provides a facility for us to keep the variables which are extended to the scope of our inner_func along with the function. That means, the free variable message is also binded with the inner_func in the environment of inner_func. This setup of binding the function, its possible variables and the free variables to an independent environment which is not interfered by other enviroments is called a Closure. So the take away is that a Closure closes over the free variables from the environment.

image-center

Decorators

A decorator is a mother function that takes another father function as arguement, add some functionality in the mother function, and returns a baby function :D All these happens without altering the source code of the original father function. It is an application of Closure.

Let’s see what hell just defined :D

def mother_function(father_function):
    def wrapper_function():
        baby_function = father_function()
        return baby_function
    return wrapper_function

# Let's say out father_function is display 

def father_display():
    print("North remembers!")

# Our output is a function baby_display which adds additional functionality is mother_function 
# by passing father_display

baby_function = mother_function(father_display)

baby_function()
--> Tell them the north remembers!

So what just happened and what is need for all these acrobatics you’ve been seeing so far?

Answer is simple. Decoration helps us to add functionalities like father_function to our mother_function by just adding them in inside wrapper_function inside the mother_function.

Here our father_function is father_display and note that we didn’t touch the father_display to make it a part of mother_function. This gives us the freedom to define our own father_functions and easily add that functionality to a bulk mother_function. The output baby will have the functionalities of all the fathers, oh wait!! that was not I meant …

Now this is not the Syntactic Sugar way of doing it. So what is sugary way? :D

@mother_function
def display():
    print("North remembers!")
    
display()
--> Tell them the north remembers!

This is same as the syntax,

display = mother_function(display)

Yet, we’ve not passed any parameters to the any functions other than mother_function, what if we need to add something like these two functions to the same decorator? Let’s try that :)

def mother_function(father_function):
    def wrapper_function():
        baby_function = father_function()
        return baby_function
    return wrapper_function

@mother_function
def display():
    print("North remembers!")
@mother_function
def display_info(name):
    print("North remembers! - {}".format(name))

display_info()

image-center

display_info("Arya")

image-center

No luck :(

So what is wrong ? It’s simple. The wrapper_function is defined with no parameters, but we’ve a parameter for display_info called name. So how we manage all these things ? We’ll modify the wrapper_function a bit.

def mother_function(father_function):
    def wrapper_function(*args, **kwargs):
        baby_function = father_function(*args, **kwargs)
        return baby_function
    return wrapper_function

@mother_function
def display_info(name):
    print("North remembers! - {}".format(name))
    
display_info("Arya")
Tell them the north remembers! - Arya

*args and **kwargs are standard ways in python to pass any number of positional or keyword arguements to a function.

Decorators are not just applicable to functions, but classes too. You may follow the same procedure for that. You may use multiple decorators for a single function, maybe efficiently for logging and this discussion would be a good start for all those advanced applications.

I’ve written this small preamble in favor of my Flask tutorial, because routing, the main concept of Flask is a great example of decorators.

Thanks ! Happy Learning !

Sleeba Paul

Sleeba Paul

Dreamer | Learner | Storyteller

comments powered by Disqus
rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora