python装饰器

一般装饰器

为什么要用装饰器:

先看一个例子:

下面是一个寻找2-num之间的质数, 并打印其耗时的代码

def cout_prime_number(num):
    t1 = time.time()
    prime_number = []
    for i in range(2, num):
        cout = 0
        for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
            if i % j == 0:
                cout += 1      
        if cout == 0:
            prime_number.append(i)
    t2 = time.time()
    print('耗时:', t2-t1)
    return prime_number


a = cout_prime_number(100)
print(a)
输出:
耗时: 0.0005745887756347656
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

若我们又有一个函数,是寻找2-nums的偶数, 并打印其耗时, 我们耗时代码又要在首尾写一遍,可读性较差,相当于写了重复代码

t1 = time.time()

t2 = time.time()

prnt('耗时:', t2-t1)

这个时候我们用装饰器,来把那些重复的代码装饰起来,方便调用,如下:

import time

def display(func):
    def wrapper(num):
        t1 = time.time()
        ret = func(num)
        t2 = time.time()
        print('耗时:', t2-t1)
        return ret
    return wrapper

@display
def cout_prime_number(num):
    prime_number = []
    for i in range(2, num):
        cout = 0
        for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
            if i % j == 0:
                cout += 1      
        if cout == 0:
            prime_number.append(i)

    return prime_number

a = cout_prime_number(100)
print(a)
输出:
耗时: 0.0005745887756347656
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

其中我们新增加了一个装饰器display,他是一个函数,而且返回的也是一个函数,可以看出,我们cout_prime_number这个函数中的耗时计算代码已经没有了,被我们写到了装饰器display里,装饰器display接收的参数func就是cout_prime_number这个函数,返回的是wrapper这个函数,当然wrapper也可以取其他的名字,而wrapper函数接收的参数就是func函数的参数num,

外面这样写wrapper(num), 里面也要写func(num), 这里func就是cout_prime_number, 有时候不确定cout_prime_number参数的个数,可以用*args和**kwargs代替,wrapper里面的参数要和func里面保持一致,即wrapper(*args,**kwargs), 里面也要写func(*args,**kwargs), 并且要把func的函数值ret返回, 而display要把wrapper返回, 反正格式就这样写,例如:

import time

def display(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        ret = func(*args, **kwargs)
        t2 = time.time()
        print('耗时:', t2-t1)
        return ret
    return wrapper

@display
def cout_prime_number(num):
    prime_number = []
    for i in range(2, num):
        cout = 0
        for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
            if i % j == 0:
                cout += 1      
        if cout == 0:
            prime_number.append(i)

    return prime_number

a = cout_prime_number(100)
print(a)
输出:
耗时: 0.0005745887756347656
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

在cout_prime_number函数前面加@display就是装饰器的用法,这其实等价于如下插入的代码段:

就是这样两句:

cout_prime_number = display(cout_prime_number )
a = cout_prime_number(100)

装饰器就是把函数cout_prime_number 作为参数传进display这个函数中

import time

def display(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        ret = func(*args, **kwargs)
        t2 = time.time()
        print('耗时:', t2-t1)
        return ret
    return wrapper


def cout_prime_number(num):
    prime_number = []
    for i in range(2, num):
        cout = 0
        for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
            if i % j == 0:
                cout += 1      
        if cout == 0:
            prime_number.append(i)

    return prime_number

cout_prime_number = display(cout_prime_number )
a = cout_prime_number(100)
print(a)
输出:
耗时: 0.0005745887756347656
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

带输入参数的装饰器

这种,要对装饰器再包装一次,display变成了三层,照着这样写就行

比如,我们想在在打印时间的同时,还打印文本text,例如:

import time

def display(text):
    def decorate(func):
        def wrapper(*args, **kwargs):
            t1 = time.time()
            ret = func(*args, **kwargs)
            t2 = time.time()
            print('text:', text)
            print('耗时:', t2-t1)
            return ret
        return wrapper
    return decorate

@display('2022年12月9日晚测试')
def cout_prime_number(num):
    prime_number = []
    for i in range(2, num):
        cout = 0
        for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
            if i % j == 0:
                cout += 1      
        if cout == 0:
            prime_number.append(i)

    return prime_number

a = cout_prime_number(100)
print(a)
输出:
text: 2022年12月9日晚测试
耗时: 0.0009658336639404297
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

上面带输入参数的装饰器用法相当于:

cout_prime_number = display('2022年12月9日晚测试')(cout_prime_number)

a = cout_prime_number(100)

print(a)

一些注意的地方,我看一般都会在warpper前面加上一句:

@wraps(func)

wraps是python包functools自带的

说是不加,有些依赖函数签名的代码执行就会出错。那也就是说一般情况可以不加?最好还是加一下。

原因:装饰器 - 廖雪峰的官方网站

import time
from functools import wraps


def display(text):
    def decorate(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            t1 = time.time()
            ret = func(*args, **kwargs)
            t2 = time.time()
            print('text:', text)
            print('耗时:', t2-t1)
            return ret
        return wrapper
    return decorate

def display3(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        t1 = time.time()
        ret = func(*args, **kwargs)
        t2 = time.time()
        print('耗时:', t2-t1)
        return ret
    return wrapper

你可能感兴趣的:(python,python)