Python Decorators

Decorator functions which modify the functionality of other functions, without modifying the source code of that function or class. Decorators are implemented as functions or classes that take another function or class as an input, and return a new function or class that wraps the original one, adding the desired functionality.

Python decorator will allow to perform operation before and after the function call, can be used in authentication, logging, rate limiting.

Here is an example of a simple decorator that prints before and after the function:

1def myDecorator(func): 2 def wrapper(*args, **kwargs): 3 print("IN") 4 result = func(*args, **kwargs) 5 print("OUT") 6 return result 7 return wrapper 8 9@myDecorator 10def myFunction(): 11 print("myFunction") 12 13myFunction() 14 15Output: 16IN 17myFunction 18OUT

In this example, the myDecorator decorator is a function that takes another function as an input (func) and returns a new function (wrapper) that wraps the original function, adding the desired functionality. The wrapper function prints IN before and OUT after. The @myDecorator syntax is used to apply the decorator to the myFunction function.

Decorators can also be applied to classes, using the same syntax. In this case, the decorator function takes a class as an input, and returns a new class that wraps the original one, adding the desired functionality. Decorators can be used to add functionality such as logging, input validation, or access control to functions and classes.

Modify argument and return values of the function using decorator

1def myDecorator(func): 2 def wrapper(*args, **kwargs): 3 print("args:", args) 4 print("kwargs:", kwargs) 5 6 # modify arguments 7 args_value = (args[0] + 10, ) 8 kwargs_value = {"B": kwargs["B"] + 10} 9 # make call to my_function 10 result = func(*args_value, **kwargs_value) 11 print("result:", result) 12 13 # modify return 14 return result[0]+2, result[1]+2 15 return wrapper 16 17@myDecorator 18def my_function(A, B=20): 19 return A*2, B*2 20 21print(my_function(100, B=200)) 22 23Output: 24args: (100,) 25kwargs: {'B': 200} 26result: (220, 420) 27(222, 422)

It's worth noting that in Python, decorators are applied from the bottom up, meaning that if you have multiple decorators applied to a function or class, the innermost decorator will be executed first.

1def myDecorator1(func): 2 def wrapper(*args, **kwargs): 3 print("IN1") 4 result = func(*args, **kwargs) 5 print("OUT1") 6 return result 7 return wrapper 8 9def myDecorator2(func): 10 def wrapper(*args, **kwargs): 11 print("IN2") 12 result = func(*args, **kwargs) 13 print("OUT2") 14 return result 15 return wrapper 16 17@myDecorator1 18@myDecorator2 19def myFunction(): 20 print("myFunction") 21 22myFunction() 23 24Output: 25IN1 26IN2 27myFunction 28OUT2 29OUT1