BlogsDope image BlogsDope

Python Decorators - The simple way

Dec. 21, 2017 FUNCTION FACTORIAL PYTHON 722

Prerequisites:

The decorator is a very powerful design in Python. It is used to decorate a function or even a class, or more precisely, modify it. But before going into decorators, let's brush up some important concepts which are used in decorators.

The first concept is that the name of the function is the reference to the function and we can also give more than one name to the same function. This is explained in the example given below.

def foo():
    print ("I am foo")
a = foo
a()

Output
I am foo

Also, nesting of functions is an important concept for decorators. You can read about it from here.

Let's look at another important concept, which is passing a function as a parameter to another function.

def foo(a):
    print ("I am foo")
    print ("Calling another function")
    a()
def f():
    print ("I am f")
foo(f)

Output
I am foo
Calling another function
I am f

The next important concept is a function returning another function. Let's take an example. You can also look at this post for better understanding.

def f1():
    print ("Hey, f1 here")
    def f2():
        print ("I am f2")
    return f2
    
a = f1()
a
a()

Output
Hey, f1 here
I am f2

Let's combine all the concepts mentioned above.

def f1(a):
    def f2(b):
        print ("Calling a")
        a(b)
        print ("a has been called")
    return f2

def foo(x):
    print ("Hey, foo here. And I am", x)

foo("xyz")

f = f1(foo)
f("abc")

We have just used the concepts discussed above in this example.

f = f1(foo) - Here, we have called f1 with foo. As we know that the environment of creation is remembered in the nested function, so the function f2(returned from f1) will remember that f1 was called with foo. Now, f("abc") is simply calling the function f2 with "abc". If you are facing any difficulty in understanding this example, then we suggest you read a post on nested functions first.

Decorators


You are now ready to make decorators. A decorator is the same thing discussed in the above example. But instead of making the variable 'f' equal to the function (f1(foo)), if we write foo=f1(foo) then this will change (modify) the function itself. And that's it, we have created a decorator. Let's look at an example.

def f1(a):
    def f2(b):
        print ("Calling a")
        a(b)
        print ("a has been called")
    return f2

print ("foo before decoration")
def foo(x):
    print ("Hey, foo here. And I am", x)

foo("xyz")

foo = f1(foo)
print ("foo after decoration")
foo("abc")

Output
foo before decoration
Hey, foo here. And I am
xyz
foo after decoration
Calling a
Hey, foo here. And I am
abc
a has been called

This concept seems quite interesting, and Python also provides a special symbol (@) to implement decorators. So, let's rewrite the above example using @.

def f1(a):
    def f2(b):
        print ("Calling a")
        a(b)
        print ("a has been called")
    return f2

@f1
def foo(x):
    print ("Hey, foo here. And I am", x)

foo("abc")

Output
Calling a
Hey, foo here. And I am
abc
a has been called

So, instead of foo=f1(foo), we have just written @f1 just before defining the function f1. So, the syntax is something like:

@decorator
def function_to_be_decorated:
    function_definition

Now, let's sum up the things we have discussed till now. Initially, we used nested function and the outer function was taking another function as its argument(the function to be decorated). We used the inner function to implement modifications to the function to be decorated. Then we used the @ symbol just above the function to be decorated.

Use of decorators


There can be various uses of decorators, one such use is that we can implement a decorator to check if a person is logged in or not. Then we can use this decorator to restrict some part of any app where logging in is compulsory for the users who are not logged in. Let's implement a simple decorator to check if a number is not negative before calculating its factorial.

def non_negative(foo):
    def check(x):
        if x>=0:
            return foo(x)
        else:
            return "The number is negative"
    return check

@non_negative
def fact(x):
    if x==0 or x==1:
        return 1
    else:
        return x*(fact(x-1))

print (fact(1))
print (fact(2))
print (fact(3))
print (fact(4))
print (fact(5))
print (fact(0))
print (fact(-1))
print (fact(-2))

Output
1
2
6
24
120
1
The number is negative
The number is negative

Liked the post?
Developer and founder of CodesDope.
Editor's Picks
0 COMMENT

Please login to view or add comment(s).