• Python3 装饰器


    一 什么是装饰器呢?

    装饰器本质上来说就是函数,功能就是为其它函数添加附加功能。 

    原则:

    1. 不修改被修饰函数的源代码
    2. 不修改被修饰函数的调用方式

    组织结构:

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

    实例1(Python3.0+):

    # 使用高阶函数试图完成装饰器
    import time
                    
    # 定义函数foo
    def foo():
        time.sleep(3)
        print('你好啊,林师傅')
    
    # 定义函数test来装饰foo函数,计算foo函数的运行时间
    def test(func):
        start_time = time.time()
        print(func)
        func()
        stop_time=time.time()
        print('函数的运行时间:%s' %(stop_time-start_time))
    
    test(foo)
    # 改变了被修饰函数(foo())的调用方式,不满足装饰器的特性,所以不是合格的装饰器
    

    实例2(Python3.0+):

    简单装饰器的创建思路

    import time
    # 先架设装饰器的架子:高阶函数+函数嵌套+闭包
    def timer(func): # func=test
        def wrapper():
            start_time=time.time()
            # print(func)
            func() # 就是在运行test()
            stop_time=time.time()
            print('函数的运行时间:%s' %(stop_time-start_time))
        return wrapper
    
    def test():
        time.sleep(2)
        print('函数运行结果')
    
    # test返回的就是wrapper
    test=timer(test)   
    # 执行test()就相当于执行wrapper()
    test()
    '''
    以上例子实现了装饰器的部分功能: 
    1、没有改变原有函数的源代码,但是改变了函数的调用方式
    '''
    
    # 思考:怎么才能不改变被修饰函数的调用方式呢,我们可以采用语法糖的方式。
    # 语法糖  @
    # @timer 就相当于test=timer(test)
    
    import time
    # 架设装饰器的架子:高阶函数+函数嵌套+闭包
    def timer(func): # func=test
        def wrapper():
            start_time=time.time()
            # print(func)
            func() # 就是在运行test()
            stop_time=time.time()
            print('函数的运行时间:%s' %(stop_time-start_time))
        return wrapper
    
    @timer
    def test():
        time.sleep(2)
        print('函数运行结果')
    
    test()
    
    # 如果我想打印被修饰函数的返回值呢?:
    # 实例1:
    #
    import time
    # 装饰器的架子:高阶函数+函数嵌套+闭包
    def timer(func): # func=test
        def wrapper():
            start_time=time.time()
            # print(func)
            func() # 就是在运行test()
            stop_time=time.time()
            print('函数的运行时间:%s' %(stop_time-start_time))
        return wrapper
    
    @timer # test = timer(test)
    def test():
        time.sleep(2)
        print('函数运行结束')
        return '这是test的返回值'
    
    res=test()
    # test()实际上运行的是wrapper() 原因:1.@timer 等于 test=timer(test) ,所以实际上运行test()相当于timer()(),也就是test=wrapper
    print(res)
    # 实际上打印的是wrapper的返回值,但wrapper函数没有ruturn返回值,所以得到的结果为None
    
    '''
    函数运行结束
    函数的运行时间:2.0001144409179688
    None
    '''
    
    # 实例1中没有实现打印被修饰函数返回值的功能,看看以下代码呢;
    # 实例2:
    # 
    import time
    # 装饰器的架子:高阶函数+函数嵌套+闭包
    def timer(func): # func=test
        def wrapper():
            start_time=time.time()
            res=func()
            # print(func)
            # func() # 就是在运行test()
            stop_time=time.time()
            print('函数的运行时间:%s' %(stop_time-start_time))
            return res
        return wrapper
    
    @timer # test = timer(test)
    def test():
        time.sleep(2)
        print('函数运行结束')
        return '这是test的返回值'
    
    print(test())
    '''
    打印结果:
    函数运行结束
    函数的运行时间:2.0001142024993896
    这是test的返回值
    '''  

    实例3(Python3.0+):

    实例2中虽然已经实现了装饰器的功能,但是被修饰的函数是没有形参的,如果我们在被修饰的函数中定义形参呢,那实例2中的装饰器就不能完成这个任务了吧??

    # 带参数的装饰器
    # 
    import time
    # 装饰器的架子:高阶函数+函数嵌套+闭包
    def timer(func): # func=test
        def wrapper(*args,**kwargs):  # 相当于定义函数 def test(*args,**wargs):
            start_time=time.time()
            res=func(*args,**kwargs) # 就是在运行test()
            # print(func)
            # func() # 就是在运行test()
            stop_time=time.time()
            print('函数的运行时间:%s' %(stop_time-start_time))
            return res
        return wrapper  # test=wrapper
    
    @timer # test = timer(test)
    def test(name,age):
        time.sleep(2)
        print('函数运行结束')
        print('test函数运行完毕,名字是%s 年龄是%s' %(name,age))
        return '这是test的返回值'
    
    # test()
    print(test('lvcm',19))
    
    '''
    运行结果:
    函数运行结束
    test函数运行完毕,名字是lvcm 年龄是19
    函数的运行时间:2.0001144409179688
    这是test的返回值
    '''

    二 练习题:

    1、模仿京东商城登录

    # 简单的京东登录架构
    # 进入京东主页
    def index():
        print('欢迎来到京东主页')
    
    # 进入个人主页
    def home(name):
        print('欢迎回家%s' %name)
    
    # 打印购物车物品
    def shopping_car(name):
        print('%s购物车里有[%s,%s,%s]'%(name,'丝袜','娃娃','奶茶'))
    
    # 打印个人订单
    def order():
        print('您的订单是.......')
    
    index()
    home('lvcm')
    shopping_car('lvcm')
    order()
    
    # 需求:给每个函数加上一个用户登录的验证功能
    # 
    # 定义用户默认登录状态
    dict_user={'username':None,'login':False}
    
    # 定义用户登录验证装饰器
    def auth_login(func):
        def wrapper(*args,**kwargs):
            # print('您未登录,请先登录,谢谢...')
    
            while True:
                if  dict_user['username']=='lvcm' and dict_user['login'] == True:
                    res=func(*args,**kwargs)
                    return res
                user_name=input('请输入用户名:').strip()
                user_passwd=input('请输入密码:').strip()
                if  user_name=='lvcm' and user_passwd=='123':
                    print('%s登录成功' %user_name)
                    dict_user['username']=user_name
                    dict_user['login']=True
                    break
                else:
                    print("用户名密码错误,请重新输入")
            res=func(*args,**kwargs)
            return res
        return wrapper
    
    @auth_login
    def index():
        print('欢迎来到京东主页')
    
    @auth_login
    def home(name):
        print('欢迎回家%s' %name)
    
    @auth_login
    def shopping_car(name):
        print('%s购物车里有[%s,%s,%s]'%(name,'丝袜','娃娃','奶茶'))
    
    @auth_login
    def order():
        print('您的订单是.......')
    
    index()
    '''
    运行结果:
    请输入用户名:lvcm
    请输入密码:123
    lvcm登录成功
    欢迎来到京东主页
    '''
    home('lvcm')
    '''
    运行结果:
    请输入用户名:lvcm
    请输入密码:123
    lvcm登录成功
    欢迎回家lvcm
    '''
    shopping_car('lvcm')
    '''
    运行结果:
    请输入用户名:lvcm
    请输入密码:123
    lvcm登录成功
    lvcm购物车里有[丝袜,娃娃,奶茶]
    '''
    order()
    '''
    运行结果:
    请输入用户名:lvcm
    请输入密码:123
    lvcm登录成功
    您的订单是.......
    '''  

    上述例题中我们模仿了京东个人登录验证功能,但是代码中发现一个很有意思的问题,就是没次刷新页面时都要验证登录,这是很不合理的,其实登录验证一次就可以了。

    2、复杂的仿京东程序

    # 定义用户列表
    user_list=[
        {'name':'lvzf','passwd':'123'},
        {'name':'zhangjm','passwd':'123'},
        {'name':'lvcm','passwd':'123'},
        {'name':'muyb','passwd':'123'},
    ]
    
    # 定义用户登录初始状态
    current_dict={'username':None,'login':False}
    
    def auth_login(func):
        def wrapper(*args,**kwargs):
            # print('您未登录,请先登录,谢谢...')
    
            while True:
                if  current_dict['username'] and current_dict['login']:
                    res=func(*args,**kwargs)
                    return res
                username=input('请输入用户名:').strip()
                userpasswd=input('请输入密码:').strip()
                for user_dict in user_list:
                    if  username==user_dict['name'] and userpasswd==user_dict['passwd']:
                        print('%s登录成功' %username)
                        current_dict['username']=username
                        current_dict['login']=True
                        break
                else:
                    print("用户名密码错误,请重新输入")
                res=func(*args,**kwargs)
                return res
        return wrapper
    
    
    print('before is ')
    
    @auth_login
    def index():
        print('欢迎来到京东主页')
    
    @auth_login
    def home(name):
        print('欢迎回家%s' %name)
    
    @auth_login
    def shopping_car(name):
        print('%s购物车里有[%s,%s,%s]'%(name,'丝袜','娃娃','奶茶'))
    
    @auth_login
    def order():
        print('您的订单是.......')
    
    index()
    home('lvcm')
    shopping_car('lvcm')
    order()
  • 相关阅读:
    27. 为什么线程执行要调用start而不是直接run
    25. ThreadLocal的使用场景
    23. 线程如何退出结束
    20. Java字符串格式化方法
    21. 时间的格式化方法
    19. 用过spring的线程池还是java的线程池?
    17. zookeeper的实现机制,有缓存,如何存储注册服务的
    面试-spring 那些事
    Apache服务器和tomcat服务器有什么区别?
    JNDI 和JDBC的区别
  • 原文地址:https://www.cnblogs.com/lvcm/p/9638752.html
Copyright © 2020-2023  润新知