How To Use Python Decorators

Introduction

In this article, you will learn about Python Decorators, different types of Python decorators, and how we can use them in Python.

Python

Python is an interpreted, high-level, general-purpose programming language created by Guido van Rossum and first released in 1991. He started Python as a hobby project to keep him occupied in the week around Christmas. It got its name from the name of the British comedy troupe Monty Python. It is used in:

  1. Software Development
  2. Web Development
  3. System Scripting
  4. Mathematics

To learn how to program in Python, visit Python Basics.

You can visit C# Corner Python Learn Series.

Python Decorators

A decorator is a Python design pattern that allows a user to add additional functionality to an existing object without changing its structure. Decorators are often summoned prior to the specification of the function to be decorated. These are used to alter the function's behavior. Decorators allow you to wrap another function in order to extend the functionality of the wrapped function without permanently changing it.

Decorators have several use cases such as:

  • Authorization in Python frameworks such as Flask and Django
  • Logging
  • Measuring execution time
  • Synchronization

Why are decorators useful?

Decorators are an important programming pattern and if used wisely, can provide a lot of benefits. It makes code very reusable and binds added functionality to functions, hence keeping code DRY.

Given below is an example, how we can use decorators in Python.

# Create an @authenticated decorator that only allows   
# the function to run is user1 has 'valid' set to True:  
test_user = {  
    'name': "C# Corner",  
    'valid': True  
}  
  
another_user = {  
  'name': "C#Corner",  
  'valid': False  
}  
  
def authenticated(fn):  
  def wrapper(*args, **kwargs):  
    if args[0]['valid']:  
      fn(args)  
  return wrapper  

@authenticated  
def deco_r(user):  
    print('message has been sent')  
  
deco_r(test_user) # message has been sent  
deco_r(another_user) # (Does nothing)  

The above "authenticated" decorator function only invokes the message_friends function based on the specified condition. This gives a lot of flexibility and performs conditional operations based on the status of the user’s authentication.

Defining Decorators

There are various ways in which we can utilize decorators in python.

1. Decorating functions with parameters

Here we pass the function, which needs to be decorated, to the decorator function. After which we make the required function call, with the parameter, to produce the output.

def remailnder(x,y):  
    print(x%y)  

def deco_r(func):  
    def inner(x,y):  
        if(x<y):  
            x,y = y,x  
        return func(x,y)  
    return inner  

result = deco_r(remailnder)  
result(25,3)  

The output of the code will be 1.

2. Using Syntactic Decorator (@)

Here instead of passing the function to be decorated to the decorator function, we write "@" followed by the decorator function name before the function definition (which needs to be decorated).

def deco_r(func):  
    def inner(x,y):  
        if(x<y):  
            x,y = y,x  
        return func(x,y)  
    return inner  

@deco_r
def remailnder(x,y):  
    print(x%y)  

remailnder(25,3)  

The output of the code will be 1.

3. Using Decorators between files

We can define a decorator in one python file, and use it in another file. Let's take an example, to better understand.

def deco_r(function):  
    def wrapper():  
        func = function()
        uppercase = func.upper()
        print(uppercase)
        return uppercase
    return wrapper

We write the above code, in test.py. 

from test import deco_r

@deco_r
def print_c():  
    return 'csharpcorner'

print_c()

The above code, when executed results in "CSHARPCORNER" as output.

4. Applying multiple decorators to a single function

def deco_r(function):  
    def wrapper():  
        func = function()
        uppercase = func.upper()
        return uppercase
    return wrapper 

def deco_s(function):
    def wrapper():
        func = function()
        splitted_string = func.split()
        return splitted_string
    return wrapper
    
@deco_s   
@deco_r
def print_c():  
    return 'csharp corner'

print(print_c())

In the above code, we apply two decorators to the print_c(). And when we print, we get ['CSHARP', 'CORNER'].

5. Passing arguments to Decorators

def deco_r(func):  
    def inner(x,y):  
        if(x<y):  
            x,y = y,x  
        return func(x,y)  
    return inner  

@deco_r
def remailnder(x,y):  
    print(x%y)  

remailnder(25,3)  

In the above code, we pass parameters to the deco_r() which then will be passed to inner(). The output of the code will be 1.

6. General-Purpose Decorators

args and **kwargs are used to define a generic decorator that can be applied to any function. All positional and keyword arguments are collected and stored in the args and **kwargs variables. args and kwargs let us send as many arguments as we like during function calls.

Following is a general-purpose decorator, which prints the passed positional and keyword arguments.

def deco_r(function_to_decorate):
    def deco_s(*args,**kwargs):
        print('The positional arguments are', args)
        print('The keyword arguments are', kwargs)
        function_to_decorate(*args)
    return deco_s

Now we will test this decorator, using the following,

  • Without Positional and Keyword Arguments

    @deco_r
    def function_with_no_argument():
        print("No arguments here.")
    
    function_with_no_argument()

    The output of the above code will be,

    The positional arguments are ()
    The keyword arguments are {}
    No arguments here.

  • Without Keyword Arguments

    def function_with_arguments(a, b, c):
        print(a, b, c)
    
    function_with_arguments(1,2,3)

    The output of the above code will be,

    The positional arguments are (1, 2, 3)
    The keyword arguments are {}
    1 2 3

  • Without Positional Arguments

    @deco_r
    def function_with_keyword_arguments():
        print("This has shown keyword arguments")
    
    function_with_keyword_arguments(first_name="CSharp", last_name="Corner")

    The output of the above code will be,

    The positional arguments are ()
    The keyword arguments are {'first_name': 'CSharp', 'last_name': 'Corner'}
    This has shown keyword arguments

7. Class as decorator

To create a decorator as a class, we must utilize a class's __call__ function. When a user wishes to build an object that functions as a function, the function decorator must return an object that behaves like a function, which is where __call__ comes in.

class MyDecorator:
	def __init__(self, function):
		self.function = function
	
	def __call__(self):
		self.function()

@MyDecorator
def function():
	print("c# corner".upper())

function()

The output of the above code will be "C# CORNER".

The above code demonstrates, how we can create and use a class decorator. One can add any code inside the __call__(), both before and after the function call.

  • Class Decorators with *args and **Kwargs

    class MyDecorator:
    	def __init__(self, function):
    		self.function = function
    	
    	def __call__(self, *args, **kwargs):
    		self.function(*args, **kwargs)
    
    @MyDecorator
    def function(x, y):
        if(x<y):  
            print(y) 
        else:
            print(x)
    
    function(5, 7)

    The output of the above code will be 7.

  • Class Decorator with a return statement

    class MyDecorator:
    
    	def __init__(self, function):
    		self.function = function
    
    	def __call__(self, *args, **kwargs):
    		# before function
    		result = self.function(*args, **kwargs)
    
    		# after function
    		return result
    
    @MyDecorator
    def function(x, y):
        if(x<y):  
            print(y) 
        else:
            print(x)
    
    function(5, 7)
    

    The output of the above code will be 7.

I have used the same logic for both of them so that you get a better understanding of the concept.

Conclusion

In this article, we discussed what are Python Decorators, different types of Python decorators, and how we can use them in Python. Do try out the code and make tweets to enhance your understanding, and comment with your views on how useful this article was.

Visit C# Corner to find answers to more such questions.

If you have any questions regarding any other technology do have a look at the C# Corner Technology Page.



C# Corner
MVP Program Director