Python Decorators

In this article, we are going to discuss Python Decorators.

Definition: Decorator is a design pattern in Python. It is a function that takes another function as an argument, add some functionality to it without modifying it, and returns another function.

This is called using “(@)” and placed before defining a function that we want to decorate.

syntax:

@decorator name
Function definition

For understanding decorators, we need to know the below concepts.
Functions are first-class objects. It means a function can be passed as an argument, can be returned from another function, can be assigned to a variable, can be defined in another function. For a better understanding, see the below examples.

  1. A function can be passed as an argument
    Ex:
    def increment(n):
      return n + 1

    def demo_funcall (function):
      num = 5
      return function(num)

    demo_funcall (increment)

    Here increment function passed as an argument

    example1.py:

    Output:

    >> python example1.py

  2. Function can be returned from another function
    Ex:
    def wish():
        def say_wish():
          return "Happy Birthday"
        return say_wish

    hello = wish()
    hello()

    example2.py:

    Output:

    >>python example2.py

    Here say_wish function returned from the wish function

  3. Function can be modified and assigned to a variable
    Ex:
    def add(a,b):
           return a +b

    sum2nos = add # Here function add assigned to variable
    sum2nos(5,11)

    example3.py:

    Output:
    >> python example3.py

  4. Define function inside another function
    Ex:
    def add(a,b):
            def sum2(a,b):
                return a + b
            res = sum2(a,b)
            return res
    add(10,15)

    example4.py:

    Output:
    >> python example4.py

Closure:

Python allows a nested function to access the outer scope of the enclosing function.

def greeting(message):
    "Enclosong Function"
    def send_greeting():
        "Nested Function"
        print(message)
    send_greeting()

greeting("Good morning")

example5.py:

Output:

>> python example5.py

After understanding the above concepts now, we will write a decorator example.

Ex1: Here, we will decorate the message function. Printing the msg inside **** without modifying the original function, i.e., message function.

#decorator start
def print_msg(function):
    def wrapper():
        function()
    return wrapper
#decorator end

def message():
    print(“This is first example for demonstrating decorator”)

hello = print_msg(message)
hello()

example6.py:

Output:

>> python example6.py

In the simplest form, we can place decorator on top of the function definition and call the function as shown below:

Here whatever string we want to decorate inside ***, use this decorator.

Output:

Multiple decorator:

We can have multiple decorator for a single function. Here the decorator is applied in the order we called.
syntax:
@decorator2
@decorator1
Function definition

Here 1st decorator will be applied, then 2nd decorator.

Passing arguments to decorator functions:

We can pass arguments to the wrapper function. The arguments passed to the function for which we want to decorate.

Ex:

def deco_wish(function):
    def wrapper (arg1, arg2):
        print (‘The passed arguments are ’,arg1, arg2)
        print (‘*********************’)
        function (arg1, arg2)
        print (‘*********************’)
    return wrapper

@deco_wish
def wish(a1, a2):
    print(a1,a2)
wish (‘Good’, ’Morning’)
wish (‘Good’, ’Afternoon’)

example7.py:

Output:

>> python example7.py

Pass variable number of arguments to decorator function:

We can pass any number of arguments using *args (Non-keyword arguments like numbers) and **kwargs (keyword arguments like a dictionary). Both are positional arguments and stores the arguments in args and kwargs variables.

Note: Here, we can use any name instead of args and kwargs, but these names are recommended to use.

Ex:

def dec_var_args(funtion):
    def wrapper(*args, **kwargs):
        print(‘The non keyword arguments are’, args)
        print(‘The keyword arguments are’, kwargs)
        function(*args)
    return wrapper

@ dec_var_args
def fun_non_key_args(*args):
    for I in args:
        print(i)

@ dec_var_args
def fun_key_args():
    print(“Keyword arguments”)

fun_non_key_args((4,5,6))
fun_key_args(fname=’Anand’, lname=’Math’)

example8.py:

Output:

>> python example8.py

Ex2: Suppose we have 2 function
Function1: Calculate the sum of numbers from the given list
Function2: Multiply each number by 2 and add them to the given list of numbers
If we want to calculate the time taken by each for execution, can do it in 2 ways

  1. Place code in between the start and end time in each function
  2. Write decorator for calculating time

See below code solved using decorator:

#decorator start
exe_time_calc(func):
    def wrapper(arg):
        start_time = datetime.datetime.now()
        func(arg)
        end_time = datetime.datetime.now()
        print ("The time take for executing function " + func.__name__  + " is " + str(end_time - end_time))
    return wrapper
#decorator end

@exe_time_calc
def cal_avg(data):
    sum = 0
    for i in data:
        sum += i
    print ("The average of given list of numbers is ", sum//len(data))

@exe_time_calc
def mul_by_2(data):
    sum = 0
    for i in data:
        sum += + (i*2)
    print ("The sume of all numbers after multiply by 2 is ", sum)

cal_avg ([10,20,30,40,50])
mul_by_2([10,20,30,40,50])

example9.py:

Output:

>> python example9.py

The above decorator can be used for calculating execution time for any of the functions. By using a decorator, we can avoid repeated code when we have a requirement for calculating the execution time to place the decorator above the function definition.

Conclusion:

Decorators change the functionality of a function/method without changing the original code of the function is being decorated. Using this, we can avoid writing repeated code. Knowing the decorator concept will make us strong in python. We can use decorator in the below cases:

  • Authorization in Python frameworks Ex: Flask and Django
  • Logging
  • Measure execution time


from Linux Hint https://ift.tt/3lK7ftU

Post a Comment

0 Comments