Decorators in Python

by: George El., March 2019, Reading time: 2 minutes

In this post I will explain decorators in python. This is a rather advanced topic. You should first read about First class functions and Closures, in previous post.

As we saw in previous post, functions can accept functions as arguments and return functions. Lets see an example.

def outerf(fn):
    from datetime import datetime
    def innerf(*args, **kwargs):
        print (datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f'))
        return fn(*args, **kwargs)
    return innerf

def add(a,b):
    return a+b

f = outerf(add)
print(f(1,2))
print(f(4,5))

We declare an outer function outerf that returns an innner function innerf. This function accepts any number of arguments. Then we declare another function called add, and we pass it as argument to outerf. This will return innerf and we assign it to f. Now when I call f(1,2), the innerf(1,2) will be called, which will print the time that the function is called and then call the original function add(1,2). This will print:

2019-03-20 16:51:25.641962
3
2019-03-20 16:51:25.642961
9

So we have altered the functionality of the add method to one that displays a message first and then returns the addition of the two numbers.

Instead of doing f = outerf(add) we could also do add = outerf(add) Now if I execute add(10,11) I will get

2019-03-20 16:53:14.604568
21

the whole code is like that

def outerf(fn):
    from datetime import datetime
    def innerf(*args, **kwargs):
        print (datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f'))
        return fn(*args, **kwargs)
    return innerf

def add(a,b):
    return a+b

add = outerf(add)
print(add(10,11))

We say that the outerf is a decorator and decorates our function add. because this happens often in python it has a special character @ that allows to decorate a function.

Instead of writing

add = outerf(add) 

we can write

@outerf
def add(a,b):
    return a+b

So the whole code is

def outerf(fn):
    from datetime import datetime
    def innerf(*args, **kwargs):
        print (datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f'))
        return fn(*args, **kwargs)
    return innerf

@outerf
def add(a,b):
    return a+b

print(add(10,11))
print(add(30,20))

and the output is

2019-03-20 16:53:44.505049
21
2019-03-20 16:53:44.505550
50

I can pass any function to it. Not just add. Let pass another function

@outerf
def sq(x):
    return x*x

print(sq(4))

the output will be

2019-03-20 16:54:48.264029
16

Decorators are used extensively in python. The important thing to understand is that decorators are just wrapper functions that add some extra functionality to our functions. For example we can make a log decorator that logs when a function is called.

comments powered by Disqus