Python3之装饰器(Decorator)进阶

日期:2020年3月2日
作者:Commas
注释:学习就是为了忘记,接上一章《Python3之装饰器(Decorator)浅谈》,现在讲一下Python装饰器进阶;
如果您想了解更多有关Python的知识,那么请点《我的Python浅谈系列目录》


文章目录

  • 一、装饰器的进阶
  • 二、多个装饰器执行顺序


一、装饰器的进阶

书接上文,我们现在来看看装饰带参数函数的装饰器,如下:

import time


def calculate_time(f):
    """装饰器函数"""
    # (1)采用*args, **kwargs来接收参数
    # def wrapper():
    def wrapper(*args, **kwargs):
        """这是wrapper函数的说明文档"""
        start_time = time.time()
        # (2)被装饰函数f接受*args, **kwargs参数 
        # f()
        f(*args, **kwargs)
        end_time = time.time()
        delta_time = end_time - start_time
        print("{}的执行时间:{}".format(f, delta_time))
    return wrapper


@calculate_time
def func(param):
    """这是func函数的说明文档"""
    time.sleep(1)
    print("执行了{}()函数,其中param={}".format(func.__name__, param))


func(1)

# =======控制台输出结果=======
执行了wrapper()函数,其中param=1
<function func at 0x000001F6E2F698C8>的执行时间:1.0006613731384277

如果被装饰的函数有返回值,那么就应该修改为:

def calculate_time(f):
    """装饰器函数"""
    def wrapper(*args, **kwargs):
        """这是wrapper函数的说明文档"""
        start_time = time.time()
        # (2)接收返回值
        # f(*args, **kwargs)
        ret = f(*args, **kwargs)
        end_time = time.time()
        delta_time = end_time - start_time
        print("{}的执行时间:{}".format(f, delta_time))
        # (3)返回返回值
        return ret
    return wrapper


@calculate_time
def func(param):
    """这是func函数的说明文档"""
    time.sleep(1)
    # (1)被装饰的函数有返回值
    # print("执行了{}()函数,其中param={}".format(func.__name__, param))
    return "执行了{}()函数,其中param={}".format(func.__name__, param)


# func(1)
ret = func(1)
print(ret)

# =======控制台输出结果=======
<function func at 0x0000019F789698C8>的执行时间:1.0038354396820068
执行了wrapper()函数,其中param=1

无论是装饰带参数函数的装饰器,还是装饰有返回值函数的装饰器,都有一个不完美的地方,那就是被装饰后的函数,函数的内置属性(如__name__)都发生了变化。如果想保证被装饰的函数做到“不变”,那么我们要不自己遍历,将对应的值更新,要么可以借助@wraps,如下:

import time
# (1)从模块functools导入wraps
from functools import wraps


def calculate_time(f):
    """装饰器函数"""
    # (2)使用wraps
    @wraps(f)
    def wrapper(*args, **kwargs):
        """这是wrapper函数的说明文档"""
        start_time = time.time()
        ret = f(*args, **kwargs)
        end_time = time.time()
        delta_time = end_time - start_time
        print("{}的执行时间:{}".format(f, delta_time))
        return ret
    return wrapper


@calculate_time
def func(param):
    """这是func函数的说明文档"""
    time.sleep(1)
    return "执行了{}()函数,其中param={}".format(func.__name__, param)


# func(1)
ret = func(1)
print(ret)

# =======控制台输出结果=======
<function func at 0x0000020C3DAC0048>的执行时间:1.0006630420684814
执行了func()函数,其中param=1

结果完美,一切OK!
但是@wraps(f)是个什么梗,还可以给装饰器传参,且待我慢慢说来,这个实际是分两部分执行,如下:

  1. "@"右边的wraps(f)先执行,并给个函数返回值,即wraps = wraps(f);
  2. 然后再执行,我们熟悉的@wraps,此时的wraps = wraps(f);

那么我们来实现一个类似的吧,@calculate_time(True),用来控制是否计算函数执行时间,如下:

import time
from functools import wraps


def calculate_time(flag):
    def inner(f):
        """装饰器函数"""
        @wraps(f)
        def wrapper(*args, **kwargs):
            """这是wrapper函数的说明文档"""
            if flag:
                start_time = time.time()
                ret = f(*args, **kwargs)
                end_time = time.time()
                delta_time = end_time - start_time
                print("{}的执行时间:{}".format(f, delta_time))
            else:
                ret = f(*args, **kwargs)
            return ret
        return wrapper
    return inner


@calculate_time(True)
def func(param):
    """这是func函数的说明文档"""
    time.sleep(1)
    return "执行了{}()函数,其中param={}".format(func.__name__, param)

ret = func(1)
print(ret)

# =======控制台输出结果=======
<function func at 0x00000162250300D0>的执行时间:1.0001797676086426
执行了func()函数,其中param=1

二、多个装饰器执行顺序

Python3之装饰器(Decorator)进阶_第1张图片

@装饰器A
@装饰器B
def 被装饰函数():
	pass

从图和代码可以知,函数的执行过程是:
1(装饰器A)→2(装饰器B)→3(装饰器B)→4(装饰器A)


版权声明:本文为博主原创文章,如需转载,请给出:
原文链接:https://blog.csdn.net/qq_35844043/article/details/104609540

你可能感兴趣的:(Python3)