 Search

# Python Decorators as Time Savers

Updated: Feb 28, 2020

It is a very normal situation that we are executing a piece of code or a function and we want to check how much time it is taking for execution or may be we have more than one techniques and want to compare which one is faster. So lets declare a function which does something useful for us. def alpha(): #In goes the code for i in range(100): print("number is :: ",i) #and then print something print("I am done, quitting function")

Now to check how much time did this function take to run, we can check the time before and after the function call and see the difference of time to know

start = datetime.datetime.now() alpha() finish = datetime.datetime.now() print("{}".format( (finish - start)))

So, this way we could see total execution time for a function. Now lets consider we have n such functions so we need to put n such ways in our code, like this

def alpha(): #In goes the code for i in range(100): print("number is :: ",i) #and then print something print("I am done, quitting function") def beta(): #In goes the code # it does blah blah for i in "read me line": print("letter is :: ",i) def gamma(): #In goes the code for i in range(100): print("number is :: ",i) def theta(): #In goes the code for i in rand.rand(100): print("number is :: ",i)

start = datetime.datetime.now() alpha() finish = datetime.datetime.now() print("{}".format( (finish - start))) start = datetime.datetime.now() beta() finish = datetime.datetime.now() print("{}".format( (finish - start))) start = datetime.datetime.now() gamma() finish = datetime.datetime.now() print("{}".format( (finish - start))) start = datetime.datetime.now() theta() finish = datetime.datetime.now() print("{}".format( (finish - start)))

Instead we can write this code in a function to make it neat looking

def time(): return datetime.datetime.now()

So, now instead of writing code for geting curent time, we can simply replace it with the function call, which is a relief. Now our code looks like

start = time() alpha() finish = time() print("{}".format( (finish - start))) start = time() beta() finish = time() print("{}".format( (finish - start))) start = time() gamma() finish = time() print("{}".format( (finish - start))) start = time() theta() finish = time() print("{}".format( (finish - start)))

Now, our code is a bit more understandable, but still we are putting so many calls, hence our code doesn't look readable. To make this type of situations readable as well as fast executable, Python provides a very good mechanism called Decorators. Lets get back to our example, how Decorators will be useful in this situation. For this we have to first write a decorator

def time(func): def inner(): start = datetime.datetime.now() func() finish = datetime.datetime.now() print("time ===>> {}".format( (finish - start))) return inner

Here time() is a Decorator, which has another function as its arguement. Inner is a nested function inside time(), the code inside is exactly as per our requirement for checking the total elapsed time in any function func. So, in our case time() does the two things it executes the elapsed time in execution of a function and invokes the function call. Now we have declare the Decorator function, now we dont need to call the time() function explicitly, we just need to assign it as a wrapper for the alpha() function

@time def alpha(): #In goes the code sum=0 for i in range(1000): sum+=i print("number is :: ",i) print("Sum is :: ",sum)

So, now we have attached the time decorator with alpha, so now when ever we will invoke our function alpha(), it will be wrapped around the time decorator.

alpha()

In our case before and after executing the alpha or any other function using time decorator, the start and finish time of that function is noted in between the execution of that particular function. So, to summarise with the help of decorators we can alter the functionality of function without modifying it. Final code with decorator for all the functions is

@time def alpha(): #In goes the code sum=0 for i in range(1000): sum+=i print("number is :: ",i) print("Sum is :: ",sum) @time def beta(): #In goes the code for i in "read me line": print("letter is :: ",i) @time def gamma(): #In goes the code for i in range(100): print("number is :: ",i) @time def theta(): #In goes the code for i in range(100): print("number is :: ",i)

alpha() beta() gamma() theta()

Decorators dynamically alter the functionality of a function, method, or class without having to directly use sub-classes or change the source code of the function being decorated. In short, decorator takes in a function, adds some functionality and returns it. Using decorators in Python also makes code more readable. Decorators have several use cases such as:

Authorization in Python frameworks such as Flask and Django, Logging, Measuring execution time and various syntax related work, for example converting all small cases to upper cases or vice versa.