zoukankan      html  css  js  c++  java
  • 【笔记】Python基础五:装饰器

    一,什么是装饰器

    本质就是函数,功能是为其他函数添加附加功能

    原则:

    1,不修改被修饰函数的源代码

    2,不修改被修饰函数的调用方式

    例子:

    import time
    
    def timmer(func):
        def wapper(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)
            stop_time = time.time()
            print('函数的运行时间 %s' % (stop_time - start_time))
            return res
        return  wapper
    
    @timmer      #加装饰器
    def cal(l):  #原函数
        res = 0
        time.sleep(1)
        for i in l:
            res += i
        return res
    
    t1 = cal(range(100))
    print(t1)
    '''输出:
    函数的运行时间 1.0000572204589844
    4950
    '''

    二,装饰器的知识储备

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

    (一)高阶函数

    定义:

    1,函数接受的参数是一个函数名

    2,函数的返回值是一个函数名

    3,满足上述条件任意一个,都可称之为高阶函数

    例子:高阶函数

    def foo():
        print('你好啊林师傅')
    
    def test(func):
        print(func) #打印内存地址
        func()      #执行函数foo
    
    test(foo) #test是高阶
    '''输出:
    <function foo at 0x00000000022F22F0>
    你好啊林师傅
    '''

    例子:函数接受的参数是一个函数名,就能实现给函数增加功能的目标

    def foo():
        time.sleep(3)
        print('你好啊林师傅')
    
    def test(func):
        start_time = time.time()
        func()      #执行函数foo
        stop_time = time.time()
        print('函数的运行时间 %s' % (stop_time - start_time))
    
    test(foo) #修改了函数原来的调用方式,原来是foo()
    '''输出:
    你好啊林师傅
    函数的运行时间 3.000171661376953
    '''

    例子:如果再加上返回值是一个函数,则能实现“不修改被修饰函数的调用方式”这一个条件

    def foo():
        print('from the foo')
    def test(func):
        return func
    
    # res = test(foo)
    # print(res) #res得到的是foo的内存地址
    # res()      #实际是运行foo
    foo = test(foo) #如果把函数的执行结果重新赋值给foo函数名
    foo()           #就能实现“不修改被修饰函数的调用方式”这一个条件
    '''输出:
    from the foo
    '''

    例子:多运行一次,并不合格(高阶函数并不能全部满足装饰器的需要)                1,不修改foo源代码,2,不修改foo调用方式

    def foo():
        time.sleep(3)
        print('from the foo')
    
    def timer(func):
        start_time = time.time()
        func()      #执行函数foo
        stop_time = time.time()
        print('函数的运行时间 %s' % (stop_time - start_time))
        return func
    
    foo = timer(foo) 
    foo()           #又执行了一次函数foo  
    '''输出:
    from the foo
    函数的运行时间 3.007171869277954
    from the foo
    '''

     (二),函数嵌套

    定义:在函数里面定义另一个函数

    def bar():
        print('from bar')
    
    def foo():
        print('from foo')
        def test():
            pass

    函数闭包:

    闭——封装变量,包——层层嵌套。其实和函数作用域是一回事。特性:如果内层没有定义重名变量,外层的变量可以传进来。

    def father(auth_type):
        # print('from father %s' %name)
        def son():
            # name='linhaifeng_1'
            # print('我的爸爸是%s' %name)
            def grandson():
                print('我的爷爷是%s' %auth_type)#实参'filedb'从最外层一直传导进来
            grandson()
        # print(locals())
        son()
    # father('linhaifeng')
    father('filedb')

    #输出:我的爷爷是filedb

    三,装饰器框架

    使用高阶函数+函数闭包完成装饰器框架。

    个人理解:1,装饰器函数传入的原函数名func,用在最内层运行原函数,以实现和原函数一样的效果

                      2,而在原函数外层包裹的一层函数wrapper,加retunrn wapper返回的是加入新功能后的一个函数地址,在外面运行这个函数地址也就执行了加入的新功能

                      3,而@这样一个语法把新函数的地址赋值给原函数名,这样运行原函数名,就把新的功能增加了。         

    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()
    '''输出:
    test函数运行完毕
    运行时间是3.000171661376953
    '''
    
    #先使用res来接收
    # res=timmer(test)  #返回的是wrapper的地址
    # res()  #执行的是wrapper()
    
    #然后使用test原函数名替换res
    # test=timmer(test)  #返回的是wrapper的地址
    # test()  #执行的是wrapper()
    
    #最后python提供@语法等效于test=timmer(test)
    #  @timmer  就相当于 test=timmer(test)

     例子:原函数加入返回值情况

    import time
    def timmer(func):
        def wrapper():
            start_time=time.time()
            res=func()           #使用res接收返回值
            stop_time = time.time()
            print('运行时间是%s' %(stop_time-start_time))
            return res           #把返回值传回去
        return wrapper
    
    @timmer
    def test():
        time.sleep(3)
        print('test函数运行完毕')
        return '这是test的返回值'  #这里原函数test加入了返回值
    
    res=test()
    print(res)
    '''输出:
    test函数运行完毕
    运行时间是3.007171869277954
    这是test的返回值
    '''

    例子:加入参数,并且考虑原函数参数不固定的情况

    #传参的复习和回顾
    def test2(name,age,gender): #test2(*('alex',18,'male','x','y'),**{})
        #name,age,gender=('alex',18,'male','x','y')
        print(name)
        print(age)
        print(gender)
    
    def test1(*args,**kwargs):
        test2(*args,**kwargs)  #args=('alex',18,'male','x','y') kwargs={}
    
    test1('alex',18,gender='male') #正确的传参方式
    test1('alex',18,'male')        #正确的传参方式
    test1('alex',18,'male','x','y')#多传了两个参数,报错:test2() takes 3 positional arguments but 5 were given
    #(*args,**kwargs)实现了传递可变参数的功能,在原函数有多个变体的情况下,装饰器函数可以通用
    import time
    def timmer(func):
        def wrapper(*args,**kwargs):
            start_time=time.time()
            res=func(*args,**kwargs) #可以理解为wrapper得到的参数原封不动的传进原函数
            print('args 是 %s:' %(args,))
            print('kwargs 是: %s' %kwargs)
            stop_time = time.time()
            print('运行时间是%s' %(stop_time-start_time))
            return res
        return wrapper
    
    @timmer
    def test(name,age):         #两个参数
        time.sleep(1)
        print('test函数运行完毕,名字是【%s】 年龄是【%s】' %(name,age))
        return '这是test的返回值'
    
    @timmer
    def test1(name,age,gender): #三个参数
        time.sleep(1)
        print('test1函数运行完毕,名字是【%s】 年龄是【%s】 性别【%s】' %(name,age,gender))
        return '这是test1的返回值'
    
    test('linhaifeng',age=18) #args=('linhaifeng')     kwargs={'age':18}
    print('######')
    test1('alex',18,'male')   #args=('alex',18,'male') kwargs={}

    '''输出: test函数运行完毕,名字是【linhaifeng】 年龄是【18】 args 是 ('linhaifeng',): kwargs 是: {'age': 18} 运行时间是1.0000572204589844 ###### test1函数运行完毕,名字是【alex】 年龄是【18】 性别【male】 args 是 ('alex', 18, 'male'): kwargs 是: {} 运行时间是1.0000572204589844 '''

    两个小技巧

    #1,解压序列
    a,*_,c =[10,9,8,7,6]#代表所有值
    print(a)
    print(_) #下划线_是变量
    print(c)
    '''输出:
    10
    [9, 8, 7]
    6
    '''

    #2,交换两个变量值 f1 = 1 f2 = 10 f1,f2 = f2,f1 print(f1,f2) #输出:10 1

    四,装饰器实战

    (一)使用装饰器实现验证和session功能

    user_list=[
        {'name':'alex','passwd':'123'},
        {'name':'linhaifeng','passwd':'123'},
        {'name':'wupeiqi','passwd':'123'},
        {'name':'yuanhao','passwd':'123'},
    ]
    current_dic={'username':None,'login':False}
    
    
    def auth_func(func):
        def wrapper(*args,**kwargs):
            if current_dic['username'] and current_dic['login']:
                res = func(*args, **kwargs)
                return res
            username=input('用户名:').strip()
            passwd=input('密码:').strip()
            for user_dic in user_list:
                if username == user_dic['name'] and passwd == user_dic['passwd']:
                    current_dic['username']=username
                    current_dic['login']=True
                    res = func(*args, **kwargs)
                    return res
            else:    #######这个else非常有价值,不是if的else,而是for的else
                print('用户名或者密码错误')
    
        return wrapper
    
    @auth_func
    def index():
        print('欢迎来到京东主页')
    
    @auth_func
    def home(name):
        print('欢迎回家%s' %name)
    
    @auth_func
    def shopping_car(name):
        print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃'))
    
    print('before-->',current_dic)
    index()
    print('after--->',current_dic)
    home('产品经理')
    # shopping_car('产品经理')

    五,装饰器运行流程

    1,运行@装饰器函数名,实际动作是运行装饰器函数,由于闭包函数wrapper定义在这个装饰器函数的里面,所以仅return wrapper,把wrapper的函数地址返回给原函数名(重新赋值)

    2,当程序运行到原函数名(),也就是执行原函数时,因为前面的步骤,实际跳转到wrapper函数执行。这里面巧妙的地方是,wrapper函数里面又包含一个原函数(),这回是真正执行原函数()。

    3,执行完原函数后,回到wrapper里面,原函数()下面的步骤运行。

    六,带参数的装饰器函数

    本质就是在外面再添加一层,把原装饰器函数包裹起来。原装饰器函数实现的是把原函数名传递进去在内层,带参数的装饰器函数实现传递功能性参数作用在最外层,层层嵌套。

    user_list=[
        {'name':'alex','passwd':'123'},
        {'name':'linhaifeng','passwd':'123'},
        {'name':'wupeiqi','passwd':'123'},
        {'name':'yuanhao','passwd':'123'},
    ]
    current_dic={'username':None,'login':False}
    
    def auth(auth_type='filedb'):
        def auth_func(func):
            def wrapper(*args,**kwargs):
                print('认证类型是',auth_type)
                if auth_type == 'filedb':
                    if current_dic['username'] and current_dic['login']:
                        res = func(*args, **kwargs)
                        return res
                    username=input('用户名:').strip()
                    passwd=input('密码:').strip()
                    for user_dic in user_list:
                        if username == user_dic['name'] and passwd == user_dic['passwd']:
                            current_dic['username']=username
                            current_dic['login']=True
                            res = func(*args, **kwargs)
                            return res
                    else:
                        print('用户名或者密码错误')
                elif auth_type == 'ldap':
                    print('鬼才特么会玩')
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('鬼才知道你用的什么认证方式')
                    res = func(*args, **kwargs)
                    return res
    
            return wrapper
        return auth_func
    
    @auth(auth_type='filedb') #auth_func=auth(auth_type='filedb')-->@auth_func 附加了一个auth_type  --->index=auth_func(index)
    def index():
        print('欢迎来到京东主页')
    
    @auth(auth_type='ldap')
    def home(name):
        print('欢迎回家%s' %name)
    #
    @auth(auth_type='sssssss')
    def shopping_car(name):
        print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃'))
    
    # print('before-->',current_dic)
    # index()
    # print('after--->',current_dic)
    # home('产品经理')
    shopping_car('产品经理')
  • 相关阅读:
    Novell 被收购
    NetBeans IDE 7.0 Beta 发布
    关于去除PE文件中函数修饰的做法
    甲骨文宣布将于明年 7 月 28 日推 JDK 7
    PE文件格式的一些研究
    如何开发 Web 应用程序
    Novell 被收购
    如何开发 Web 应用程序
    分享:DFC开发平台的设计理念
    分享:FireBreath 1.7.0 RC1 发布
  • 原文地址:https://www.cnblogs.com/fudonghai/p/10323608.html
Copyright © 2011-2022 走看看