• Python之装饰器


     什么是装饰器?

    在说装饰器之前啊. 我们先说一个软件设计的原则: 开闭原则, 又被成为开放封闭原则,你的代码对功能的扩展是开放的你的程序对修改源代码是封闭的. 这样的软件设计思路可以更好的维护和开发。
      开放:对功能扩展开放
      封闭:对修改代码封闭

    谈装饰器前,还要先要明白一件事,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例如:

    def foo():
        print("foo")
    
    def bar(func):
        func()
    
    bar(foo)

    装饰器的目的: 在不改变原来代码的基础上. 给代码添加新功能。

     装饰器形成过程

    简单装饰器

    def func1():
        print("欢迎访问主页")
    
    # 添加一个登录验证的功能
    def wrapper(fn):
        def inner():
            print("请先登录....")
            fn()  
        return inner
    
    func1 = wrapper(func1)
    func1()
    
    # 装饰器的基本雏形
    # def wrapper(fn): # fn:目标函数.
    #     def inner():
    #         '''执行函数之前'''
    #         fn() # 执行被装饰的函数
    #         '''执行函数之后'''
    #     return inner

    来看一下,执行流程:

    语法糖

       但是如果有多个函数,都需要添加登录验证的功能,每次都得func1 = timer(func1)?这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等,所以更简单的方法,python给你提供了,那就是语法糖。 @装饰器

    def wrapper(fn):
        def inner():
            print("请先登录....")
            fn()
        return inner
    
    @wrapper  # 相当于 => func1 = wrapper(func1)
    def func1():
        print("欢迎访问主页")
    
    func1()

    有个小问题,就是查看func1函数名和函数注释都变成inner了。

    print(func1.__doc__)     # 查看函数注释信息
    print(func1.__name__)   # 查看函数名
    # 我是内部函数
    # inner
    
    如何解决???
    from functools import wraps
    def wrapper(fn):
        @wraps(fn)   # 改变inner的名字
        def inner():
            """我是内部函数"""
            print("请先登录....")
            fn()
        return inner
    
    @wrapper  # 相当于 => func1 = wrapper(func1)
    def func1():
        """ 主页函数 """
        print("欢迎访问主页")
    
    func1()
    
    print(func1.__doc__)
    print(func1.__name__)
    #  主页函数 
    # func1

    被装饰函数带参数和返回值

    from functools import wraps
    def wrapper(fn):  # fn: 目标函数, 被装饰的函数
        @wraps(fn)   # 改变inner的名字
        def inner(*args,**kwargs):  # 万能参数  *args, **kwargs: 接收参数
            """我是内部函数"""
            print("请先登录....")
            ret = fn(*args,**kwargs)      # 调用目标函数.
            """被装饰函数执行之后"""
            return ret                    # 返回结果
        return inner
    
    @wrapper  # 相当于 => func1 = wrapper(func1)
    def func1(usernamee):
        """ 主页函数 """
        print("欢迎 %s 访问主页" % usernamee)
        return True
    
    @wrapper  # 相当于 => func2 = wrapper(func2)
    def func2(name,age):
        print("名字:%s 年龄:%s" %(name,age))
        return True
    
    ret = func1("lishichao")   # 实际执行的是inner 参数传给了inner函数
    print(ret)
    
    func2("lishichao","19")

    带有参数的装饰器

    给装饰器传参,可以控制装饰器是否使用。

    def wrapper(flag):  # 装饰器参数
        def inner1(fn):  # fn: 目标函数, 被装饰的函数
            def inner2(*args,**kwargs):  # 万能参数  *args, **kwargs: 接收参数
                """我是内部函数"""
                if flag:  # 判断传入的参数  True就执行装饰器 False不执行装饰器
                    print("请先登录....")
                ret = fn(*args,**kwargs)      # 调用目标函数.
                """"""
                return ret                    # 返回结果
            return inner2
        return inner1
    
    @wrapper(False) # 执行流程: 先执行wrapper(False)  返回装饰器 再和 @ 拼接起来 @inner1
    def func1(usernamee):
        """ 主页函数 """
        print("欢迎 %s 访问主页" % usernamee)
        return True
    
    ret = func1("lishichao")   # 实际执行的是inner 参数传给了inner函数
    print(ret)

    执行流程:

    多个装饰器装饰一个函数

    def wrapper1(fn):
        def inner(*args, **kwargs):
            print("第一个装饰器开始")
            ret = fn(*args, **kwargs)
            print("第一个装饰器结束")
            return ret
        return inner
    
    def wrapper2(fn):
        def inner(*args, **kwargs):
            print("第二个装饰器开始")
            ret = fn(*args, **kwargs)
            print("第二个装饰器结束")
            return ret
        return inner
    
    def wrapper3(fn):
        def inner(*args, **kwargs):
            print("第三个装饰器开始")
            ret = fn(*args, **kwargs)
            print("第三个装饰器结束")
            return ret
        return inner
    
    @wrapper1
    @wrapper2
    @wrapper3
    def func():
        print("瞬间来三")
    
    func()
    # 执行结果
    # 第一个装饰器开始
    # 第二个装饰器开始
    # 第三个装饰器开始
    # 瞬间来三
    # 第三个装饰器结束
    # 第二个装饰器结束
    # 第一个装饰器结束

    执行流程:

    wrapper1   
    wrapper2   
    wrapper3   # 调用函数执行之前
    func
    wrapper3   # 调用函数执行之后
    wrapper2
    wrapper1

    练习题

    1、装饰器的固定格式

    from functools import wraps
    def wrapper(flag):  # 接收装饰器的参数
        def inner1(fn): # 接收的是被装饰函数的函数名
            @wraps(fn)  # 改变inner2函数的名字为被装饰函数的名字
            def inner2(*args,**kwargs):   # 无敌传参,接收参数
                if flag:  # 判断是否执行装饰器
                    print("被装饰函数执行之前")
                ret = fn(*args,**kwargs)  #被装饰函数, 调用参数
                print("被装饰函数执行之后")
                return ret   # 返回值
            return inner2
        return inner1
    
    @wrapper(True)
    def func1():
        print("我是被装饰函数")
        return True
    
    func1()
    print(func1.__name__)
    装饰器的固定格式

    2、编写装饰器,在每次执行被装饰函数之前打印一句’每次执行被装饰函数之前都得先经过这里,这里根据需求添加代码’

    def wrapper(fn):
        def inner(*args,**kwargs):
            print("每次执行被装饰函数之前都得先经过这里,这里根据需求添加代码")
            ret = fn(*args,**kwargs)  #调用被装饰函数,ret接收被装饰函数的返回值
            return ret
        return inner
    
    @wrapper
    def func():
        print("我是被装饰函数")
        return "被装饰函数的返回值"
    
    ret = func()
    print(ret)
    View Code

    3、编写装饰器,在每次执行被装饰函数之后打印一句’每次执行完被装饰函数之后都得先经过这里,这里根据需求添加代码’

    def wrapper(fn):
        def inner(*args,**kwargs):
            ret = fn(*args,**kwargs)
            print("每次执行完被装饰函数之后都得先经过这里,这里根据需求添加代码")
            return ret
        return inner
    
    @wrapper
    def func():
        print("我是被装饰函数")
    
    func()
    View Code

    4、编写装饰器,在每次执行被装饰函数之前让用户输入用户名,密码,给用户三次机会,登录成功之后,才能访问该函数.

    flag = False
    def login():
        num = 1
        while num <= 3:
            username = input("请输入用户名:").strip().lower()
            password = input("请输入密码:").strip().lower()
            if username == "lishichao" and password == "123":
                global flag
                flag = True
                break
            else:
                num += 1
                print("输入错误,请重新输入")
                continue
        else:
            print("输入超过三次,已锁定")
    
    
    def login_wrapper(fn):
        def inner():
            login()
            if flag:
                fn()
            else:
                print("登录失败")
        return inner
    
    @login_wrapper
    def func1():
        print("欢迎访问文章页面")
    View Code

    5、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件,只支持单用户的账号密码,给用户三次机会)要求登录成功一次,后续的函数都无需再输入用户名和密码

    import sys
    flag = False
    
    def sign_up():
        with open("user_info",mode="w",encoding="utf-8") as f:
            username = input("请输入注册账号:").strip()
            password = input("请输入注册密码:").strip()
            f.write("%s|%s
    " %(username,password))
            print("注册成功,请登录")
    
    def login():
        num = 1
        while num <= 3:
            username = input("请输入账号:").strip()
            if username == "":
                print("账号不能为空")
                continue
            password = input("请输入密码:").strip("|")
            if password == "":
                print("密码不能为空,请重新输入")
                continue
            num += 1
            with open("user_info", mode="r", encoding="utf-8") as f:
                for i in f:
                    user_list = i.strip().split("|")
                    if username == user_list[0] and password == user_list[1]:
                        print("登录成功")
                        global flag
                        flag = True
                        return
                else:
                    print("登录失败,账号或密码不正确.")
        else:
            print("===========================================")
            print("登录超过三次,账号已锁定")
            print("===========================================")
            sys.exit()
    
    
    def wrapper(fn):
        def inner(*args,**kwargs):
            if flag:
                ret = fn(*args,**kwargs)
                return ret
            else:
                login()
        return inner
    
    @wrapper
    def func1():
        print("欢迎访问文章页面")
    
    @wrapper
    def func2():
        print("欢迎访问管理页面")
    
    @wrapper
    def func3():
        print("欢迎访问评论页面")
    
    dic = {
        "1":sign_up,
        "2":login,
        "3":func1,
        "4":func2,
        "5":func3
    }
    
    def main():
        while 1:
            print("""
               请选择:
                    1.注册
                    2.登录
                    3.访问文章页面
                    4.访问管理页面
                    5.访问评论页面
                    [q] 退出
            """)
            n = input(">>>").strip().lower()
            if n == "q":
                break
            else:
                dic[n]()
    
    main()
    View Code

    6、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件,可支持多账号密码)要求登录成功一次(给三次机会),后续的函数都无需再输入用户名和密码。

    import sys
    flag = False
    
    def sign_up():
        with open("user_info",mode="a",encoding="utf-8") as f:
            username = input("请输入注册账号:").strip()
            password = input("请输入注册密码:").strip()
            f.write("%s|%s
    " %(username,password))
            print("注册成功,请登录")
    
    def login():
        num = 1
        while num <= 3:
            username = input("请输入账号:").strip()
            if username == "":
                print("账号不能为空")
                continue
            password = input("请输入密码:").strip("|")
            if password == "":
                print("密码不能为空,请重新输入")
                continue
            num += 1
            with open("user_info", mode="r", encoding="utf-8") as f:
                for i in f:
                    user_list = i.strip().split("|")
                    if username == user_list[0] and password == user_list[1]:
                        print("登录成功")
                        global flag
                        flag = True
                        return
                else:
                    print("登录失败,账号或密码不正确.")
        else:
            print("===========================================")
            print("登录超过三次,账号已锁定")
            print("===========================================")
            sys.exit()
    
    
    def wrapper(fn):
        def inner(*args,**kwargs):
            if flag:
                ret = fn(*args,**kwargs)
                return ret
            else:
                login()
        return inner
    
    @wrapper
    def func1():
        print("欢迎访问文章页面")
    
    @wrapper
    def func2():
        print("欢迎访问管理页面")
    
    @wrapper
    def func3():
        print("欢迎访问评论页面")
    
    dic = {
        "1":sign_up,
        "2":login,
        "3":func1,
        "4":func2,
        "5":func3
    }
    
    def main():
        while 1:
            print("""
               请选择:
                    1.注册
                    2.登录
                    3.访问文章页面
                    4.访问管理页面
                    5.访问评论页面
                    [q] 退出
            """)
            n = input(">>>").strip().lower()
            if n == "q":
                break
            else:
                dic[n]()
    
    main()
    View Code

    7、给每个函数写一个记录日志的功能
    功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。
    所需模块:
    import time
    struct_time = time.localtime()
    print(time.strftime("%Y‐%m‐%d %H:%M:%S",struct_time))

    def wrapper_log(flag):
        def wrapper(fn):
            def innrt(*args,**kwargs):
                if flag:
                    import time
                    struct_time = time.localtime()
                    Time = time.strftime("%Y-%m-%d %H:%M:%S",struct_time)
                    func_name = fn.__name__
                    with open("func_accecc.log",mode="a",encoding="utf-8") as f:
                        f.write("在 %s    执行函数: %s
    " %(Time,func_name))
                ret = fn(*args,**kwargs)
                return ret
            return innrt
        return wrapper
    
    @wrapper_log(True)
    def func1():
        print("欢迎访问文章页面")
    
    @wrapper_log(True)
    def func2():
        print("欢迎访问评论页面")
    
    @wrapper_log(True)
    def func3():
        print("欢迎访问管理页面")
    
    func1()
    func2()
    func3()
    View Code
  • 相关阅读:
    SQLServer 2008数据库查看死锁、堵塞的SQL语句
    Jmeter(三)简单的HTTP请求(非录制)
    watir中不能打开页面中的URL超链接解决办法
    我要搬博客到这里来,请协助
    Jmeter(一)精简测试脚本
    性能测试机中存在大量的TIME_WAIT状态的连接,影响并发压力的发起
    ruby+watir随机而不重复获取Menu菜单的元素
    Eclipse中安装Ruby的插件org.rubypeople.rdt
    TCP连接各状态数量、以及TCP各状态变迁流程
    ruby+watirwatir3.0上实现快照/截图
  • 原文地址:https://www.cnblogs.com/root0/p/10307600.html
Copyright © 2020-2023  润新知