zoukankan      html  css  js  c++  java
  • 装饰器

    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
    
    
  • 相关阅读:
    C#学习笔记
    Visual Studio 快捷键
    java 8 中lambda表达式学习
    Spfa算法
    dijkstra算法
    topSort
    并查集--学习详解
    trie树--详解
    POJ1988 并查集的使用
    Mybatis的一级缓存和二级缓存
  • 原文地址:https://www.cnblogs.com/cx59244405/p/8459023.html
Copyright © 2011-2022 走看看