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:
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:
@type
def func():
return 42
print (func)
@print
def func2():
return 42
print (func2)
|
Output:
<class 'function'>
<function func2 at 0x7f135f067f28>
None
You can replace decorated object with something else
Example 4:
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”.
Whether you're preparing for your first job interview or aiming to upskill in this ever-evolving tech landscape,
GeeksforGeeks Courses are your key to success. We provide top-quality content at affordable prices, all geared towards accelerating your growth in a time-bound manner. Join the millions we've already empowered, and we're here to do the same for you. Don't miss out -
check it out now!
Last Updated :
11 Dec, 2019
Like Article
Save Article