Useful cases to illustrate Decorators in python
A decorator is a special kind of function that either takes a function and returns a function or takes a class and returns a class. Well, it can be any callable (i.e functions, classes, methods as they can be called) and it can return anything, it can also take a method. This is also called metaprogramming, as a part of the program tries to modify another part of the program at compile time.
Let’s dive into python decorators and find out what they can do. This will not be covering the basics or decorators with parameters, but some useful examples to illustrate the case.
Refer the below article to get the basics of Python decorators
Basically, a decorator takes in a callable, any object which implements the special method __call()__
is termed as callable, adds some functionality and returns a callable.
Example 1:
# Python program to demonstrate # decorators # Creating a decorator def decorated_func(func): def inner(): print ( "This is decorated function" ) func() return inner() def ordinary_func (): print ( "This is ordinary function" ) decorated = decorated_func(ordinary_func) decorated |
Output:
This is decorated function This is ordinary function
In the example shown above, decorated_func()
is a decorator. In short, a decorator acts as a wrapper that wraps an object (does not alter the original object) and adds an new functionality to original object. This is a common construct, so Python has a syntax feature (called Decorator
) to simplify this. For example,
This:
@decorated_func def ordinary_func(): print("This is ordinary function")
is Equivalent to:
def ordinary_func(): print("This is ordinary function") decorated = decorated_func(ordinary_func)
A simple example would be:
Example 2:
Input:
def mul_decorator(func): def wrapper( * args, * * kwargs): print ( 'function' , func.__name__, 'called with args - ' , / args, 'and kwargs - ' , kwargs) result = func( * args, * * kwargs) print ( 'function' , func.__name__, 'returns' , result) return result return wrapper @mul_decorator def mul(a, b): return a * b mul( 3 , 3 ) mul( 3 , b = 6 ) |
Output:
function mul called with args - (3, 3) and kwargs - {} function mul returns 9 function mul called with args - (3,) and kwargs - {'b': 6} function mul returns 18
You can also use the built-ins as decorators
Example 3:
# func will be func = type(func) -> <class 'function'> @type def func(): return 42 print (func) # print doesn't return anything, so func == None @print def func2(): return 42 # Prints None print (func2) |
Output:
<class 'function'> <function func2 at 0x7f135f067f28> None
You can replace decorated object with something else
Example 4:
# Creating a decorator class function_1: def __init__( self , func): self .func = func self .stats = [] def __call__( self , * args, * * kwargs): try : result = self .func( * args, * * kwargs) except Exception as e: self .stats.append((args, kwargs, e)) raise e else : self .stats.append((args, kwargs, result)) return result @classmethod def function_2( cls , func): return cls (func) @function_1 .function_2 def func(x, y): return x / y print (func( 6 , 2 )) print (func(x = 6 , y = 4 )) func( 5 , 0 ) print (func.stats) print (func) |
Output:
3.0 1.5
Traceback (most recent call last): File "/home/1ba974e44c61e303979b3ee120b6b066.py", line 29, in func(5, 0) File "/home/1ba974e44c61e303979b3ee120b6b066.py", line 11, in __call__ raise e File "/home/1ba974e44c61e303979b3ee120b6b066.py", line 8, in __call__ result = self.func(*args, **kwargs) File "/home/1ba974e44c61e303979b3ee120b6b066.py", line 23, in func return x / y ZeroDivisionError: division by zero
Notice how the original "func"
was replaced by an instance of "function_1"
, which can be used in the same way as the original function.
You can create relation with other objects in system
Example 5:
def dict_from_func(func): return {func.__name__: func} activity = {} @activity .update @dict_from_func def mul(a, b): return a * b @activity .update @dict_from_func def add(a, b): return a + b print (mul) print (activity) print (activity[ 'mul' ]( 2 , 5 )) |
Output:
None {'mul': <function mul at 0x7f0d2209fe18>, 'add': <function add at 0x7f0d220a2158>} 10
Here, in the example 5, we have used dict.update
method as a decorator, even if it is not intended for this. This, is possible because dict_from_func
returns a dict, and dict.update
takes a dict as an argument.
Actually, This:
@activity.update @dict_from_func def mul(a, b): return a * b
Equals this –
def mul(a, b): return a * b mul = activity.update(dict_from_func(mul))
Conclusion
Decorators is an interesting and amazing feature and can be used for variety of purposes. It’s not just “function or class that takes function or a class and returns a function or a class”.
Contact Us