最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Python之装饰器如何使用

    正文概述    2020-01-20   221

    Python之装饰器如何使用

    装饰器的使用

    来看下面的代码:

        import time
        import random
        
        def index():
            time.sleep(random.randrange(1,5))
            print("welcome to index page")
        
        index()

    index函数的作用是程序在随机睡眠1到5秒之后,打印一句话。现在想为index函数添加一个新功能:统计index函数的运行时间,该怎么做呢??

    修改index函数如下:

        import time
        import random
        
        def index():
            start_time=time.time()
            time.sleep(random.randrange(1,5))
            print("welcome to index page")
            end_time=time.time()
            print("cost time: %s" %(end_time - start_time))
        
        index()

    运行程序,执行结果如下:

    welcome to index page
    cost time: 2.000999927520752

    可以看到,为index函数添加新功能确实实现了,但是却违反了开放封闭原则。在符合开放封闭原则的前提下,如果想为index函数添加新功能,此时就要使用装饰器了。

    修改代码

      import time
      import random
        
        def index():
            time.sleep(random.randrange(1,5))
            print("welcome to index page")
        
        def timmer():
            def inner():
                start_time=time.time()
                index()
                end_time=time.time()
                print("run time: %s " %(end_time-start_time))
            return inner
        
        f=timmer()
        f()

    运行程序,查看执行结果

    welcome to index page
    run time: 1.0

    从程序执行结果可以看出,index函数的运行时间已经被统计出来了,但是查看源码可以知道,index函数的源码确实没有被修改,但是index的调用方式被修改了。而且还有一个问题就是,timmer这个装饰器只能被用来装饰index这个函数,如果以后想统计别的函数的运行时间,又要重新定义别的装饰器,这样也太不灵活了。

    修改上面的代码

        import time
        import random
        
        def timmer(func):
            def inner():
                start_time=time.time()
                func()
                end_time=time.time()
                print("run time: %s " %(end_time-start_time))
            return inner
            
        def index():
            time.sleep(random.randrange(1,5))
            print("welcome to index page")
        
        index=timmer(index)
        index()

    运行程序,查看程序执行结果

    welcome to index page
    run time: 4.0

    可以看到,index函数的源代码没有被修改,index函数的调用方式也没有改变,但是依然为index函数添加了统计时间的功能,这里使用的就是装饰器了。

    来分析下上面代码的执行流程:

        1.导入time和random模块,定义index函数和timmer函数。

        2.把原始的index函数的内存地址作为参数传给timmer函数。

        3.timmer函数内部嵌套定义一个函数inner,然后返回inner函数的内存地址。

        4.timmer函数执行完成,返回timmer函数的内部函数inner的内存地址,然后把inner的内存地址赋值给index变量。

        5.index是inner函数的内存地址,index变量加括号运行,实际上就是在运行inner函数。

        6.运行inner函数,定义程序开始时间。

        7.执行timmer函数的变量func,在第2步知道,func这个变量就是index的内存地址,所以这里实际上是执行被装饰过后的index函数。

        8.index函数执行完成,定义程序的终止时间。

        9.统计并打印整个程序的执行过程中所花费的时间。

    这就是装饰器装饰index函数的执行流程。

    相关推荐:《Python视频教程》

    装饰器的简化使用

    现在我又有另外一个函数home,现在我也想统计home函数的运行时间,可以把代码修改如下:

        import time
        import random
        
        def timmer(func):
            def inner():
                start_time=time.time()
                func()
                end_time=time.time()
                print("run time: %s " %(end_time-start_time))
            return inner
        
        def index():
            time.sleep(random.randrange(1,5))
            print("welcome to index page")
        
        def home():
            time.sleep(random.randrange(1,5))
            print("welcome to home page")
            
        index=timmer(index)
        index()
        
        home=timmer(home)
        home()

    运行程序,执行结果如下:

    welcome to index page
    run time: 3.0 
    welcome to home page
    run time: 4.0

    可以看到,每次调用统计程序运行时间的装饰器timmer,都要先把被调用的函数的函数名作为参数传给timmer装饰器,然后再把timmer装饰器的执行结果赋值给被调用的函数名本身,最后才能调用被装饰的函数,太麻烦了有没有??

    其实python中的装饰器可以简化成下面的格式:

        import time
        import random
        
        def timmer(func):
            def inner():
                start_time=time.time()
                func()
                end_time=time.time()
                print("run time: %s " %(end_time-start_time))
            return inner
        
        @timmer
        def index():
            time.sleep(random.randrange(1,5))
            print("welcome to index page")
        
        @timmer
        def home():
            time.sleep(random.randrange(1,5))
            print("welcome to home page")
        
        index()
        home()

    程序执行结果:

    welcome to index page
    run time: 2.0 
    welcome to home page
    run time: 4.0

    可以看出,使用@加装饰器名添加到被装饰对象的上方的方式也可以为一个函数添加装饰器中定义的功能。

    多个装饰器的定义与调用

    在上面的例子里,定义并调用了一个统计程序运行时间的装饰器timmer,如果现在想为index函数添加一个用户认证的功能,可以定义一个名为auth的装饰器。

        import time
        import random
        
        def auth(func):
            def wrapper():
                while True:
                    user=input("Input your username>>>:").strip()
                    pwd=input("Input your password>>>:").strip()
                    if user== "abcd" and pwd == "abcd1234":
                        print("login successful")
                        func()
                        break
                    else:
                        print("login error")
            return wrapper
        
        @auth
        def index():
            time.sleep(random.randrange(1,5))
            print("welcome to index page")
        
        index()

    运行程序:

        Input your username>>>:abcd             # 先输入错误的用户名和密码
        Input your password>>>:1234
        login error                             # 提示用户输入错误,登录失败
        Input your username>>>:abcd             # 让用户再次输入用户名和密码
        Input your password>>>:abcd1234
        login successful                        # 登录成功
        welcome to index page                   # 执行index函数

    从程序执行结果可以看出,用户登录密码验证的装饰器auth已经定义并被成功调用了。

    如果想为index函数添加用户认证的功能,又想统计index函数执行时间的功能,在使用装饰器的情况下该怎么调用呢?

        import time
        import random
        
        def timmer(func):
            def inner():
                start_time=time.time()
                func()
                end_time=time.time()
                print("run time: %s " %(end_time-start_time))
            return inner
        
        def auth(func):
            def wrapper():
                while True:
                    user=input("Input your username>>>:").strip()
                    pwd=input("Input your password>>>:").strip()
                    if user== "abcd" and pwd == "abcd1234":
                        print("login successful")
                        func()
                        break
                    else:
                        print("login error")
            return wrapper
        
        @timmer
        @auth
        def index():
            time.sleep(2)
            print("welcome to index page")
        
        index()

    在上面的代码里,为index函数添加了两个装饰器,现在有一个问题,就是这两个装饰器究竟哪个先被调用,哪个后被调用呢??

    来分析一下,如果timmer装饰器先被调用,那么程序就会先执行timmer装饰器,然后再执行auth装饰器,提示输入用户名和密码,这样一来timmer装饰器统计的时间就会包括输入用户名和密码的时间,这个时间会远远大于index函数睡眠的2秒种;如果auth装饰器先被调用,timmer装饰器后被调用,那么timmer装饰器统计的运行时间就应该只包括index函数的执行时间值应该在2秒多一点点的时间范围内。

    运行程序,先输入错误的用户名和密码以使用程序的执行时间加长。

    Input your username>>>:abcd
    Input your password>>>:abcd
    login error
    Input your username>>>:abcd
    Input your password>>>:abcd1234
    login successful
    welcome to index page
    run time: 12.759000062942505

    从程序的执行结果可以知道,程序是先运行timmer装饰器,然后才运行auth装饰器,所以timmer统计的时间就包括了用户认证的时间,所以timmer统计到的程序运行时间远远大于index睡眠的2秒钟。

    所以这里得出一个结论:

    当一个函数同时被两个装饰器装饰时,加上函数最上面的装饰器先执行,加在下面的装饰器先装饰。

    把上面例子里的timmer装饰器和auth装饰器位置互换一下。

        import time
        import random
        
        def timmer(func):
            def inner():
                start_time=time.time()
                func()
                end_time=time.time()
                print("run time: %s " %(end_time-start_time))
            return inner
        
        def auth(func):
            def wrapper():
                while True:
                    user=input("Input your username>>>:").strip()
                    pwd=input("Input your password>>>:").strip()
                    if user== "abcd" and pwd == "abcd1234":
                        print("login successful")
                        func()
                        break
                    else:
                        print("login error")
            return wrapper
        
        @auth
        @timmer
        def index():
            time.sleep(2)
            print("welcome to index page")
        
        index()

    运行index函数,依然先输入错误的用户名和密码,增加用户认证的时间。

    Input your username>>>:abcd
    Input your password>>>:abcd
    login error
    Input your username>>>:abcd
    Input your password>>>:abcd1234
    login successful
    welcome to index page
    run time: 2.0

    可以看到,这次timmer统计到的时间只包含index函数的运行时间,不包含用户进行认证的时间。

    来分析一下上面例子中,index函数被timmer装饰器和auth装饰器装饰的代码装饰流程。

        @auth           # index=auth(timmer(index))
        @timmer         # index=timmer(index)
        def index():
            time.sleep(2)
            print("welcome to index page")

    在上面得出结论,一个函数同时被两个装饰器时,加在下面的装饰器先装饰:

    1.timmer装饰器装饰原始的index,可以写成:index=timmer(index)。

    2.在timmer装饰器中,timmer装饰器实际上是返回inner的内存地址,所以在这里,index=inner。

    3.timmer装饰器装饰完成后,由auth装饰器来装饰,此时可以写成index=auth(index)。

    4.这里auth括号里的index已经不再是原始index函数,而是已经被timmer装饰过后的index了,所以index=auth(timmer(index))。

    5.又因为timmer装饰的结果等于inner函数的内存地址,所以:index=auth(inner)。

    至此,两个装饰器的装饰过程已经知道了,来看程序的执行过程:

    6.程序先执行auth装饰器,进入用户认证,请用户输入用户名和密码。

    7.用户输入正确的用户名和密码后,开始执行func函数,也已经上面分析的inner函数。

    8.timmer装饰器先定义程序的开始运行时间,然后运行func函数,也就是原生的index函数。

    9.index函数先睡眠2秒,然后执行print语句,再定义程序的结束时间。

    10.最后统计并打印程序的运行时间,至此程序运行完毕。

    所以这里用户输入用户名和密码的时间不会被timmer装饰器统计在内。

    相关推荐:

    Python之装饰器简介


    下载网 » Python之装饰器如何使用

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元