美文网首页
Python 中的装饰器

Python 中的装饰器

作者: 李威威 | 来源:发表于2019-05-27 00:13 被阅读0次

标签:装饰器、闭包。

首先我们介绍装饰器的作用:装饰器可以在代码运行期间,动态地增加功能。

我们介绍装饰器的思路:

1、函数没有返回值,没有参数,装饰器怎么写

2、函数有返回值,没有参数,装饰器怎么写

3、 函数有返回值,有参数,装饰器怎么写

先从一个简单的例子说起,例如我们要打印 1100 的素数。

Python 代码:

def __is_prime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


def prime_nums():
    for i in range(2, 100):
        if __is_prime(i):
            print(i)


if __name__ == '__main__':
    prime_nums()

如果我想测试这段代码运行的时间,下面是一种比较容易想到的写法,但是侵入性太强了。

Python 代码:

def prime_nums():
    import time
    begin = time.time()
    
    for i in range(2, 100):
        if __is_prime(i):
            print(i)
    end = time.time()
    
    print('耗时', (end - begin))

即我想给业务逻辑增加一些辅助的、通用的代码,并且是低耦合、不修改源代码的,应该怎么做呢?使用装饰器。什么是装饰器呢?装饰器其实就是一个闭包函数。

我们下面的任务就是把“计时任务”和“打印素数”分离开来。于是,我们编写一个闭包函数。

说明:闭包是一个函数,是一个把函数作为返回值的函数。

Python 代码:

def display_time(func):
    def wrapper():
        import time
        begin = time.time()
        # 调用这个被传进来的函数
        func()
        end = time.time()
        print('耗时', (end - begin))

    return wrapper

上面的闭包函数,接收一个函数引用,并且返回了一个函数引用。

下面写上 @display_time ,这其实就是语法糖,表示运行的就是这个装饰器,把下面这个函数都传到装饰器代表的闭包中了,完整代码如下。

Python 代码:

def display_time(func):
    def wrapper():
        import time
        begin = time.time()
        # 调用这个被传进来的函数
        func()
        end = time.time()
        print('耗时', (end - begin))

    return wrapper


def __is_prime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


@display_time
def prime_nums():
    for i in range(2, 100):
        if __is_prime(i):
            print(i)

如果需要添加逻辑的函数有返回值,即我们的任务是计算 1100 的素数有多少个。

我们这里,我们想添加一个规定:1 是素数。因此,我们就要修改返回值了。我们可以在闭包函数中,增加这个逻辑,而不修改原始函数。

Python 代码:

def display_time(func):
    def wrapper():
        import time
        begin = time.time()
        # 调用这个被传进来的函数
        res = func()
        end = time.time()
        print('耗时', (end - begin))
        return res + 1

    return wrapper


def __is_prime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


@display_time
def prime_nums():
    res = 0
    for i in range(2, 100):
        if __is_prime(i):
            res += 1
    return res


if __name__ == '__main__':
    res = prime_nums()
    print(res)

总结一下:

image-20190119194159537

那如果函数有参数,装饰器有应该怎么写呢?

Python 代码:

def display_time(func):
    def wrapper(*args):
        import time
        begin = time.time()

        # 可以对参数作修改
        print(type(*args))
        new_param = args[0]
        new_param *= 100

        # 调用这个被传进来的函数
        res = func(new_param)
        end = time.time()
        print('耗时', (end - begin))
        return res + 1

    return wrapper


def __is_prime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


@display_time
def prime_nums(max_num):
    res = 0
    for i in range(2, max_num):
        if __is_prime(i):
            res += 1
    return res


if __name__ == '__main__':
    res = prime_nums(100)
    print(res)

小结一下:

image-20190119194713202

因此,使用装饰器,你可以修改参数,你也可以修改结果。

下面,如果你想使用相同的装饰器,针对不同的逻辑,有不同的调用方式,你可以给“装饰器”传递一个参数,因此,你的装饰器就要再加上一层了。

Python 代码:

def new_tips(argv):
    def tips(func):
        def nei(a, b):
            print('start {} {}'.format(argv, func.__name__))
            func(a, b)
            print('stop')

        return nei

    return tips


@new_tips('add123')
def add(a, b):
    print(a + b)


@new_tips('sub123')
def sub(a, b):
    print(a - b)


add(4, 5)
sub(8, 5)

运行结果:

image-20190119195446302

可以看到,装饰器 @new_tips('add123') 的参数传递到闭包中去了,可以 针对不同的参数执行不同的逻辑。

小结:

image-20190119195625942

参考资料

1、“正月点灯笼”的视频讲解:https://www.bilibili.com/video/av25698102?t=99

2、2道极好的Python算法题|带你透彻理解装饰器的妙用

https://zhuanlan.zhihu.com/p/26151166

3、廖雪峰老师的 Python 教程

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318435599930270c0381a3b44db991cd6d858064ac0000

相关文章

网友评论

      本文标题:Python 中的装饰器

      本文链接:https://www.haomeiwen.com/subject/jvmqtctx.html