装饰器

1、一个普通的装饰器

装饰器可以在不改变原有函数基础上增加功能

下面定义了一个增加输出函数运行时间的装饰器

import time
from functools import wraps

def timethis(func):
    @wraps(func)
    def warpper(*args,**kwargs):
        start = time.time()
        result = func(*args,**kwargs)
        end = time.time()
        print(func.__name__,end - start,sep=':')
        return result
    return warpper

@timethis
def test(n:int):
    '''
    :param n:
    :return:
    '''
    while n>0:
        n-=1

#@timethis等价于test = timethis(test)

test(1000000)
print(test.__name__)#函数名
print(test.__doc__)#注释
print(test.__annotations__)#形参类型注释
@wraps(func)可以保存原函数中的信息

@wraps(func)可以通过__wrapped__属性访问被包装的函数

(原始函数)test(n)等价于test.__wrapped__(n)

目前看来@wraps(func)好处多多,百利无害,是否可以作为标准写法无脑加呢??

2、可以接受参数的装饰器

在上面装饰器的基础上做的修改,在原本的timethis外面再套一层来接受参数

在原来的输出函数运行时间的基础上延迟t秒

import time
from functools import wraps

def timedelay(t=0):
    def timethis(func):
        @wraps(func)
        def warpper(*args,**kwargs):
            start = time.time()
            result = func(*args,**kwargs)
            end = time.time()
            print(func.__name__,end - start+int(t),sep=':')
            return result
        return warpper
    return timethis

@timedelay(3)
def test(n:int):
    '''
    test函数
    '''
    while n>0:
        n-=1
    return 'end'


test(1000000)
print(test.__name__)#函数名
print(test.__doc__)#注释
print(test.__annotations__)#形参类型注释
print(test.__wrapped__(10000))
@timedelay(3)等价于test = timedelay(3)(test)
 

3、用类的方式定义装饰器

用类的方式定义装饰器,可以得到一个可调用的实例,进而可以使用实例中的数据

下面的装饰器增加了记录函数调用次数的功能

import types
from functools import wraps

class Profield:
    def __init__(self,func):
        wraps(func)(self)
        self.ncalls = 0

    def __call__(self, *args, **kwargs):
        self.ncalls += 1
        return self.__wrapped__(*args, **kwargs)

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            return types.MethodType(self,instance)

@Profield
def add(x,y):
    return x+y

print(add(1,2))
print(add(1,2))
print(add.ncalls)


class Test:
    @Profield
    def bar(self,x):
        print(x)

t = Test()
t.bar(1)
t.bar(2)
t.bar(3)
print(Test.bar.ncalls)
@Profield等价于add=Profield(add)
wraps函数可以使用__wraps__来使用原始函数,从而不影响原始函数的基础上添加新的功能

4、在类中使用自定义装饰器

需要注意自定义装饰器要写在类/静态方法装饰器得里面

import types,time
from functools import wraps

def timedelay(t=0):
    def timethis(func):
        @wraps(func)
        def warpper(*args,**kwargs):
            start = time.time()
            result = func(*args,**kwargs)
            end = time.time()
            print(func.__name__,end - start+int(t),sep=':')
            return result
        return warpper
    return timethis

class Test:

    @staticmethod
    @timedelay(1)
    def do(n:int):
        sum = 0
        for i in range(n):
            sum+=i

Test.do(100000)

  

5、装饰器执行顺序

多个装饰器执行按照正常程序流程,自上向下。装饰器封装按照从内到外,自下而上。

所以得出结论,为了确保某些你不知道实现的装饰器正确运行,要将其写在最外面,最后再做封装,以确保不改变其功能。

例如类装饰器@staticmethod,Flask中的路由装饰器@app.route('/'),都要写在最外面。

def test1(func):
    def warpper(*args,**kwargs):
        return  "1111"+func(*args,**kwargs)+"1111"
    return warpper

def test2(func):
    def warpper(*args,**kwargs):
        return "2222"+func(*args,**kwargs)+"2222"
    return warpper

@test1
@test2
def func():
    return 'haha'

print(func())

执行结果:
11112222haha22221111

可以看出先封装test2
先执行的test1

原文地址:https://www.cnblogs.com/cx59244405/p/8459023.html