• python (七)装饰器


    一、装饰器的形成

      1、装饰器的形成(装饰不带参数的函数)  

    需求1:  用一段代码测试一下函数的执行效率

    import time
    print(time.time())  # 输出当前时间
    
    def func1():
        time.sleep(1)  # 等待1秒
        print(111)
    
    def func2():
        time.sleep(1)
        print(222)
    
    start_time = time.time()  # 开始时间
    func1()  # 执行此函数
    end_time = time.time()  # 结束时间
    print('此函数的执行时间是%s' % (end_time-start_time))  # 输出执行时间 =结束时间-开始时间
    
    
    start_time = time.time()
    func2()
    end_time = time.time()
    print('此函数的执行时间是%s' % (end_time-start_time))
    装饰器--简单版

    但是如果有多个函数,都想测试他们的执行时间,是不是每次都得func1 = timer(func1)?

    这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等

    第一版本不好,重复造轮子,测试函数的效率 必须得复制代码测试。

    改版:使用函数

    # 将代码封装到函数中,基本完成
    import time
    
    def func1():
        time.sleep(1)
        print(111)
    
    def func2():
        time.sleep(1)
        print(222)
    
    def timmer(f):
        start_time = time.time()
        f()
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time-start_time))
    timmer(func1)
    timmer(func2)
    装饰器--使用函数

    需求2: 生产环境测试函数的真正的执行效率:线上500个函数

      线上问题:
        线上的函数你不改变
        函数的原本的调用方式不能改变

      原本线上的调用方式:
        func1

        func2

      但是用了测试效率的函数之后
        def timmer(f):
        start_time = time.time()
        f()
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time-start_time))

      此时已经改变了函数的调用方式:

        func1()

        timmer(func1)

    没有改变函数里面的函数体,但是改变了函数的调用方法,所以要解决:用最少的代码,解决调用方式一致性的问题。
    最终完成的需求 func1() 这个指令既要执行func1函数,又要测试效率

    # 利用闭包,在不改变函数体的情况下
    import time
    def func1():
        time.sleep(1)
        print(111)
    
    def func2():
        time.sleep(1)
        print(222)
    
    def timmer(f): # f = func1 函数的内存地址
        def inner():
            start_time = time.time()
            f()
            end_time = time.time()
            print('此函数的执行时间是%s' % (end_time-start_time))
        return inner
    func1 = timmer(func1) # = inner
    func1()  # inner()
    
    def func1():
        print(func1)
    func1 = 777
    print(func1)
    装饰器--利用闭包

     功能实现了,但是有点复杂,还可以继续优化

    import time
    def timmer(f): # f = func1 函数的内存地址
        def inner():
            start_time = time.time()
            f()
            end_time = time.time()
            print('此函数的执行时间是%s' % (end_time-start_time))
        return inner
    
    
    def func1():
        time.sleep(1)
        print(111)
    
    
    def func2():
        time.sleep(1)
        print(222)
    
    
    def func3():
        time.sleep(1)
        print(333)
    
    func1 = timmer(func1)
    func1()  # inner()
    
    func2 = timmer(func2)
    func2()
    
    func3 = timmer(func3)
    func3()
    装饰器--闭包优化

    上面还是不够优化,python 提供了更简洁的方法--语法糖

    import time
    def timmer(f):  # f = func1 函数的内存地址
        def inner():
            start_time = time.time()
            f()
            end_time = time.time()
            print('此函数的执行时间是%s' % (end_time - start_time))
    
        return inner
    
    @timmer # func1 = timmer(func1)
    def func1():
        time.sleep(1)
        print(111)
    
    func1()  # inner()
    @timmer
    def func2():
        time.sleep(1)
        print(222)
    func2()
    
    
    def func3():  # 这个不会执行,没有加语法糖
        time.sleep(1)
        print(333)
    装饰器--语法糖

      2、装饰带参数的函数  

    import time
    
    def timmer(f):  # f = func1 函数的内存地址
        def inner(*args, **kwargs):
            start_time = time.time()
            f(*args, **kwargs)
            end_time = time.time()
            print('此函数的执行时间是%s' % (end_time - start_time))
    
        return inner
    
    @timmer  # func1 = timmer(func1)
    def func1(a):
        time.sleep(0.6)
        print('来了,%s' % (a))
    func1('大锤')

      3、装饰有返回值的函数  

    import time
    
    def timmer(f):  # f = func1 函数的内存地址
        def inner(*args, **kwargs):
            start_time = time.time()
            ret = f(*args, **kwargs)
            end_time = time.time()
            print('此函数的执行时间是%s' % (end_time - start_time))
            return ret
    
        return inner
    
    @timmer  # func1 = timmer(func1)
    def func1(a):
        time.sleep(1)
        print('来了,%s' % (a))
        return 666
    print(func1('大锤'))  # print(inner('alex'))

    小总结:  

      装饰器本质其实闭包,
      装饰器在不改变原函数的内部函数体以及调用方式的前提下,给函数增加了额外的功能:登录,注册,打印日志,函数效率等等。

      4、装饰器的基本结构  

    def wrapper(f):
        def inner(*args, **kwargs):
            ''' 执行被装饰的函数之前的操作'''
            print('执行之前', 555)
            ret = f(*args, **kwargs)
            print('执行之后', 666)
            ''' 执行被装饰的函数之后的操作'''
            return ret
        return inner
    
    @wrapper
    def func():
        pass
    
    func()

    二、装饰器的应用

      1、装饰器的固定格式  

    # 装饰器的固定格式:
    def wrapper(f):
        def inner(*args, **kwargs):
            ret = f(*args, **kwargs)
            return ret
        return inner

      2、装饰器的应用  

    # 登录认证
    
    login_status = {
        'username': None,
        'status': False,
    }
    
    
    def login(f):
        def inner(*args, **kwargs):
            if login_status['status']:
                ret = f(*args, **kwargs)
                return ret
            else:
                print('请先登录')
                username = input('请输入用户名').strip()
                password = input('请输入密码').strip()
                if username == '大锤' and password == '123':
                    login_status['username'] = username
                    login_status['status'] = True
                    ret = f(*args, **kwargs)
                    return ret
        
        return inner
    
    
    @login
    def article():
        print('欢迎访问文章页面')
    
    
    @login
    def dariy():
        print('欢迎访问日记页面')
    
    
    @login
    def comment():
        print('欢迎访问评论页面')
    
    
    dic = {
        1: login,
        2: article,
        3: dariy,
        4: comment,
    }
    
    while 1:
        print('''
        欢迎来到博客园首页:
        1,登录
        2,文章页面
        3,日记页面
        4,评论页面
        ''')
        num = input('请输入数字:').strip()
        dic[int(num)]()
    装饰器的应用-登录认证

    三、带参数的装饰器

    需求: 500个函数加上装饰器,再去掉,在加 ,再去....

    # 基本格式
    def login(a, b):
        print(a, b)
        def wrapper(f):
            def inner(*args, **kwargs):
                ret = f(*args, **kwargs)
                return ret
            return inner
        return wrapper
    
    @login(1, 2)
    def func():
        print(111)
    
    func()

    举例

    # 带参数的装饰器
    # 登录认证
    
    login_status = {
        'username': None,
        'status': False,
    }
    
    def login(a):
        def wrapper(f):
            def inner(*args, **kwargs):
                if a:
                    if login_status['status']:
                        ret = f(*args, **kwargs)
                        return ret
                    else:
                        print('请先登录')
                        username = input('请输入用户名').strip()
                        password = input('请输入密码').strip()
                        if username == '大锤' and password == '123':
                            login_status['username'] = username
                            login_status['status'] = True
                            ret = f(*args, **kwargs)
                            return ret
                else:
                    ret = f(*args, **kwargs)
                    return ret
            return inner
        return wrapper
    
    flag = False
    
    @login(flag)
    def func1():
        print(111)
    @login(flag)
    def func2():
        print(12)
    @login(flag)
    def func3():
        print(131)
    
    func1()
    带参数的装饰器--登录认证基本结构
    login_status = {
        'username': None,
        'status': False,
    }
    
    
    def login(a):
        def wrapper(f):
            def inner(*args, **kwargs):
                if login_status['status']:
                    ret = f(*args, **kwargs)
                    return ret
                else:
                    print('请先登录')
                    '''根据a 京东,天猫 去验证不同密码'''
                    username = input('请输入用户名').strip()
                    password = input('请输入密码').strip()
                    if username == '二狗' and password == '123':
                        login_status['username'] = username
                        login_status['status'] = True
                        ret = f(*args, **kwargs)
                        return ret
    
            return inner
        return wrapper
    
    
    @login('京东')
    def jd():
        print('欢迎访问文章页面')
    
    
    @login('京东')
    def jdmarkte():
        print('欢迎访问日记页面')
    
    
    @login('天猫')
    def TM():
        print('欢迎访问评论页面')
    
    @login('天猫')
    def TMmarke():
        print('欢迎访问评论页面')
    带参数的装饰器--根据不同页面验证不同密码

     进阶:多个装饰器装饰一个函数,注意顺序

    # 多个装饰器装饰一个函数
    
    def wrapper1(func):  # func = 函数名f
        def inner1():
            print('wrapper1 ,before func')  # 2
            func()  # 函数f
            print('wrapper1 ,after func')  # 4
        return inner1
    
    def wrapper2(func):  # func = inner1
        def inner2():
            print('wrapper2 ,before func')  # 1
            func()  # inner1()
            print('wrapper2 ,after func')  # 5
        return inner2
    
    
    @wrapper2  # f = wrapper2(f) 里面的f 是inner1 外面的f 是inner2
    @wrapper1  # f = wrapper1(f) 里面的f是函数名f  外面的f是 inner1
    def f():
        print('in f')  # 3
    f()  # inner2()
  • 相关阅读:
    【Node.js】Jade视图模板的使用
    【Node.js】新建一个NodeJS 4.X项目
    【Node.Js】npm国内被墙的解决方法
    【AngularJS学习笔记】Java Script "use strict" 严格模式
    【Linux学习笔记】Linux-CentOS下安装Redis
    【Linux学习笔记】常用命令速记
    【AngularJS学习笔记】封装一些简单的控件(封装成Html标签)
    数组和切片1
    Go错误处理机制及自定义错误
    内置函数
  • 原文地址:https://www.cnblogs.com/caoyinshan/p/10110506.html
Copyright © 2020-2023  润新知