Table of contents
No headings in the article.
Put simply: Decorators wrap a function, modifying its behavior.
A simple decorator example:
def my_decorator(function):
def wrapper():
print("Something is happening before the function is called.")
function()
# do other stuff
print("Something is happening after the function is called.")
return wrapper
In this example, this decorator is changing the function's behavior by doing extra stuff before and after the function call.
There are mainly two ways we can use the above decorator.
Method 1:
def say_whee():
print("Whee!")
my_decorator(say_whee)
Method 2:
@my_decorator
def say_whee():
print("Whee!")
We will use the 2nd method in upcoming examples as it’s more readable.
Example use:
we can use a decorator to add logs before and after the execution of functions.
we can use a decorator to calculate and print the execution time of functions.
Decorator for function with return value:
def my_decorator(function):
def wrapper():
print("Something is happening before the function is called.")
value = function()
# do other stuff
print("Something is happening after the function is called.", value)
return value
return wrapper
Notice that we have just added the return value we got from calling the function.
Example use:
@my_decorator
def say_whee():
print("Whee!")
return 100
Decorator for function with arguments:
def my_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_whee(name):
print("Hello, " + name)
return 100
Passing arguments to the decorator:
def exception_handler(except_returns=None): # for multiple values pass in tuple e.g (value1, value2)
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
try:
return function(*args, **kwargs)
except Exception as e:
exc_type, exc_value, exc_traceback = sys.exc_info()
message = f"Exception in {function.__name__}, {exc_value}"
print(message)
return except_returns
return wrapper
return decorator
Notice that we are accepting values in the except_returns argument. The idea is to use this value when an exception occurs.
Technical Detail: The @functools.wraps
decorator uses the function functools.update_wrapper()
to update special attributes like __name__ and __doc__ that are used in the introspection.
Use:
@exception_handler(0)
def function_with_exception(value, val2, val3):
print(a) # a is not defined
return 100
value = function_with_exception(1, 2, 3) # this will return 0
This article is a simplified version of these sources:
For ref: https://realpython.com/primer-on-python-decorators/#decorators-with-arguments
Class decorators: https://stackoverflow.com/a/9647491
decorators with args: https://stackoverflow.com/questions/5929107/decorators-with-parameters