• Python的装饰器


         装饰器

           初识装饰器,有一种高大上的感觉,但又不知道是啥。从字面意义上来看:装饰,就是起修饰作用,附加功能;器,就是函数;既然是函数,那就好说了,我先写这么一个函数。

        装饰器的两个原则:

          1.不改变原函数

          2.不改变原函数的调用方式

           怎么理解这两句话?不改变原函数,就是不动函数的代码,就给他添加一个功能;不改变原函数的调用方式也是一样。

           装饰器 = 高阶函数+函数嵌套+闭包

           这是装饰器的组成,如何一步步来实现装饰器,先看高阶函数行不行,现在我要给一个函数添加一个打印运行时间的功能,我先这样写:

    import time
    
    def foo():
        time.sleep(2)
        print('this is my house')
    
    start_time = time.time()
    foo()
    stop_time = time.time()
    total_time  = stop_time - start_time
    print ("一共用了 %s 秒"%total_time)

          这样已经实现了想要的功能,把下面这一段写成高阶函数,似乎可以大功告成

    import time
    
    def foo():
        time.sleep(2)
        print('this is my house')
    
    def timer(func):
        start_time = time.time()
        func()
        stop_time = time.time()
        total_time  = stop_time - start_time
        print ("一共用了 %s 秒"%total_time)
        return func
    
    foo = timer(foo)
    foo()

           先介绍一个高阶函数的定义,就是函数接收的参数是一个函数名或函数返回的参数是一个函数名,满足其中一个条件就是高阶函数。上面的运行结果是:

    >>> this is my house
    >>> 一共用了 2.000192165374756 秒
    >>> this is my house

         为了不影响原函数调用,我将高阶函数也赋值给foo,但是最后运行了两次,并不满足要求,看来单用高阶函数不能满足要求,配合函数嵌套和闭包试一下。先看看函数嵌套的例子:

    def father(name):
        print ('my father is %s' %name)
        def son():
            print('my son is %s' %name)  #当前函数没有name时,就往上一层找,这叫函数的作用域
            def grandson():
                name = '6788'
                print('我的爷爷是%s' %name)
            grandson()
        son()
    
    father('23455')

          这个例子演示了函数嵌套和闭包,闭包是哪个?闭包就是里面的嵌套函数。上面用高阶函数,会多运行一次,因为在foo = timer(foo)已经运行一次了,函数嵌套好像可以处理这个问题,试一下:

    import time
    def timmer(func): #func=test
        def wrapper():
            # print(func)
            start_time=time.time()
            func() #就是在运行test()
            stop_time = time.time()
            print('运行时间是%s' %(stop_time-start_time))
        return wrapper
    
    def test():
        time.sleep(3)
        print('test函数运行完毕')
    
    test=timmer(test)  #返回的是wrapper的地址
    test()  #执行的是wrapper()

           看一下运行结果:

    >>> test函数运行完毕
    >>> 运行时间是3.0001602172851562

         我去,成功了,一个简单的装饰器就这么实现了。在Python中,装饰器有自己的写法(@装饰器的函数名),就是这样的:

    import time
    def timmer(func): #func=test
        def wrapper():
            # print(func)
            start_time=time.time()
            func() #就是在运行test()
            stop_time = time.time()
            print('运行时间是%s' %(stop_time-start_time))
        return wrapper
    
    @timmer #test=timmer(test)
    def test():
        time.sleep(3)
        print('test函数运行完毕')
    
    test()

            其中@timmer就是test= timmer(test)嘛,也满足装饰器的两个原则,完美!

            但是上面的test函数式最简单的函数,如果函数有返回值呢,如果带参数呢?是不是还可以这么写?现在试一下带返回值的:

    import time
    def timmer(func): #func=test
        def wrapper():
            # print(func)
            start_time=time.time()
            func() #就是在运行test()
            stop_time = time.time()
            print('运行时间是%s' %(stop_time-start_time))
        return wrapper
    
    @timmer #test=timmer(test)
    def test():
        time.sleep(3)
        print('test函数运行完毕')
        return '返回值'
    
    print(test())

           看一下结果:

    >>> test函数运行完毕
    >>> 运行时间是3.005080461502075
    >>> None

            怎么会是None?明明有返回值啊。看一下怎么运行的,print里面test()已经运行了,在wrapper()里面并没有返回值,难怪会出现None,那我加上一个就好了:

    import time
    def timmer(func): #func=test
        def wrapper():
            # print(func)
            start_time=time.time()
            ret = func() #就是在运行test()
            stop_time = time.time()
            print('运行时间是%s' %(stop_time-start_time))
            return ret
        return wrapper
    
    @timmer #test=timmer(test)
    def test():
        time.sleep(3)
        print('test函数运行完毕')
        return ('test的返回值')
    
    print(test())

            这就OK了嘛。返回值搞定了,参数肯定更简单,我试一下加参数的:

    import time
    def timmer(func): #func=test
        def wrapper():
            # print(func)
            start_time=time.time()
            ret = func() #就是在运行test()
            stop_time = time.time()
            print('运行时间是%s' %(stop_time-start_time))
            return ret
        return wrapper
    
    @timmer #test=timmer(test)
    def test(name,age):
        time.sleep(3)
        print('test函数运行完毕,名字是%s,年龄是%s' %(name,age))
        return ('test的返回值')
    
    ret = test('dfg',12)
    print(ret)

           看一下结果:

    TypeError: wrapper() takes 0 positional arguments but 2 were given

           报错了,这可不得报错嘛,参数都不给,现在给wrapper()和func()加上参数试一下:

    import time
    def timmer(func): #func=test
        def wrapper(x,y):
            # print(func)
            start_time=time.time()
            ret = func(x,y) #就是在运行test()
            stop_time = time.time()
            print('运行时间是%s' %(stop_time-start_time))
            return ret
        return wrapper
    
    @timmer #test=timmer(test)
    def test(name,age):
        time.sleep(3)
        print('test函数运行完毕,名字是%s,年龄是%s' %(name,age))
        return ('test的返回值')
    
    ret = test('dfg',12)
    print(ret)

           运行一下:

    test函数运行完毕,名字是dfg,年龄是12
    运行时间是3.0100483894348145
    test的返回值

          没问题,很完美。但是想一下,函数都是两个参数的吗?参数有多少种?对啊,用*args和**kwargs就搞定一切啊,走起:

    import time
    def timmer(func): #func=test
        def wrapper(*args,**kwargs):
            # print(func)
            start_time=time.time()
            ret = func(*args,**kwargs) #就是在运行test()
            stop_time = time.time()
            print('运行时间是%s' %(stop_time-start_time))
            return ret
        return wrapper
    
    @timmer #test=timmer(test)
    def test(name,age):
        time.sleep(3)
        print('test函数运行完毕,名字是%s,年龄是%s' %(name,age))
        return ('test的返回值')
    
    @timmer #test=timmer(test)
    def test1(name,age,wifi):
        time.sleep(3)
        print('test函数运行完毕,名字是%s,年龄是%s,wifi是%s' %(name,age,wifi))
    
    ret = test('dfg',12)
    print(ret)
    test1('dfaaa',12,'765hsdf')

             这样就基本搞定了装饰器了,很简单嘛。但是有个问题,装饰器可以带参数吗?为什么不行?都是函数啊,试一下,做一个模拟淘宝登录流程,添加认证功能:

    import time
    
    auth_list = [
        {'username':'444','passwd':'123'},
        {'username':'555','passwd':'123'},
        {'username':'666','passwd':'123456'},
        {'username':'777','passwd':'123456'}
    ]
    wo_dic = {'username':None,'login':False}
    
    def check(auth_type='filedb'):
        def check_01(func):
    
                def wrecur(*args,**kwargs):
                    if auth_type == 'filedb':
                        if wo_dic['username'] and wo_dic['login']:
                            ret = func(*args, **kwargs)
                            return ret
    
                        username = input('用户名:').strip()
                        passwd = input('密码:').strip()
                        for i in auth_list:
                            if username == i['username'] and passwd == i['passwd']:
                                wo_dic['username'] = username
                                wo_dic['login'] = True
                                ret = func(*args, **kwargs)
                                return ret
                        else:
                            print('用户名或密码错误')
                    elif auth_type == 'otherdb':
                        print('鬼都不知道怎么用')
                    else:
                        print('写的什么鬼')
                return wrecur
        return check_01
    
    
    @check(auth_type='filedb')
    def index():
        print('欢迎来到淘宝主页')
    
    @check(auth_type='otherdb')
    def home(name):
        print('%s,欢迎回家' %name)
    
    @check(auth_type='ssssss')
    def shopping_car():
        print('购物车里有:%s,%s,%s' %('香蕉','牛奶','山羊'))
    
    index()
    home('产品经理')
    shopping_car('产品经理')

          这样就实现了不同参数的装饰器对应不同的认证方式,装饰器基本就这些知识点了。

        

  • 相关阅读:
    AppCan学习笔记----Request和登录功能简单实现
    漫谈:从APP崩溃率标准,到Monkey介绍拓展Maxim,及Jenkins自动化配置,持续集成获取崩溃monkey日志
    Android:adb shell 命令详解
    Android:adb命令详解
    常用获取Android崩溃日志和IOS崩溃日志的几种方法
    numpy 介绍和基础使用详解
    软件测试之路再谈(三年测试风雨)
    HTTP协议介绍
    Android Jenkins自动打包纪录
    Android专项测试监控资源
  • 原文地址:https://www.cnblogs.com/pengfy/p/10560410.html
Copyright © 2020-2023  润新知