ZhangZhihui's Blog  

A decorator is a callable that takes a callable as input and returns a callable. This is the general definition of a decorator. The callable in this definition can be a function or a class. In our initial discussion, we will talk about decorator functions that are used to decorate functions; we will talk about classes later on. So, for the time being, we can think of a decorator as a function that takes a function as input and returns a function.

Decorators are used to add some functionality to a function. They allow you to execute some extra code before or after the execution of a function. This extra work is done without making any changes to the source code of the function. So, by using a decorator we can extend the behavior of a function without actually modifying its code.

A decorator is a function that takes another function as an argument, decorates it with the extra functionality and gives you a decorated function as the result.

def func1():
    print('Hello world')
    print('Welcome to Python')

def my_decorator(fn):
    def wrapper():
        print('Hi … Starting execution')
        fn()
        print('Bye … finished executing\n')
    return wrapper

decorated_func1 = my_decorator(func1)

 

@my_decorator
def func3():
   print('Learning decorators')

The function func3 is decorated using the automated decoration syntax and so now we do not need to write the statement func3 = my_decorator(func3). The @ syntax automates this reassignment of the function name.

 

Decorator Example: Checking return values

Decorator Example: Checking argument values

 

Preserving metadata of a function after decoration

If we do not want the original function to lose its name, documentation and other attributes, even after decoration, then we can use the wraps function from the functools module. This wraps function is a decorator that copies the introspection details of a function to another function. We will apply this decorator to the wrapper function.

from functools import wraps

def trace(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        print(f'{fn.__name__} called')
        print(f'args : {args}  kwargs : {kwargs}' )
        result = fn(*args, **kwargs)
        print(f'Return value : {result}\n')      
        return result

    return wrapper

This wraps decorator is little different, it is a decorator that takes an argument. We will see such decorators in the next section. We send function fn as argument to this wrapper function. So now all the important metadata of fn will be copied in the wrapper function.

 

General template for writing a decorator

from functools import wraps

def decorator_name(fn):
     @wraps(fn)
     def wrapper(*args, **kwargs):
        #Place code that has to be executed before function call 
        result = fn(*args, **kwargs)
        #Place code that has to be executed after function call 
        return result

     return wrapper

 

General template for writing a decorator factory

from functools import wraps

def decorator_name(parameter1, parameter2, …):
   def actual_decorator(fn):
     @wraps(fn)
     def wrapper(*args, **kwargs):
        #Place code that has to be executed before function call 
        result = fn(*args, **kwargs)
        #Place code that has to be executed after function call 
        return result

      return wrapper

   return actual_decorator

 

Applying decorators to imported functions

We can even apply our own decorators to the functions that we import from standard library or third-party packages. However, we cannot use the @ syntax for these functions.

from math import factorial
from random import randint


def trace(fn):
    def wrapper(*args, **kwargs):
        print(f'{fn.__name__} called')
        print(f'args : {args}  kwargs : {kwargs}' )
        result = fn(*args, **kwargs)
        print(f'Return value : {result}\n')      

        return result

    return wrapper


factorial = trace(factorial)
randint = trace(randint)
factorial(3)
randint(5, 50)

 

posted on 2024-07-31 15:19  ZhangZhihuiAAA  阅读(4)  评论(0编辑  收藏  举报