zoukankan      html  css  js  c++  java
  • 装饰器函数

    阅读目录:

    • 开闭原则

    • 装饰器的概述

    • 同一个函数被多个装饰器装饰

    • 带参数的装饰器

    • 装饰器的应用

    开闭原则 设计模式六大原则之一

    又被称为开放封闭原则,主要包括以下两点:

      1.对功能的扩展开放(即可以加功能)

      2.对(源)代码的修改是封闭的(即原来的代码不能修改,为了程序的稳定性)

    总结下来就是 不改变源函数(原来的调用方式,函数名,参数,返回值),只改变函数体(仅限于对函数体增加代码,不能更改原有的代码)),就能实现新功能的扩展.

    装饰器的概述

      装饰器的本质:一个闭包函数

      装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展(在函数的前、后添加功能)

    def wrapper(fn):
        def inner():
            print('开外挂')   # 在原有函数前面加新功能
            fn()
            print('关闭外挂')  # 在原有函数后面加新功能
    
        return inner
    
    
    def play():  # 对原有函数完全没有修改
        print('双击LOL')
        print('选择狂战士')
        print('进草丛')
        print('狮子看')
        print('崩山击')
    
    
    play = wrapper(play)  # 这个需要的时候写上,不需要的时候注释掉
    play()  # 对原有的调用方式完全没有变
    装饰器--最初级版
    def wrapper(fn):
        def inner(*args, **kwargs):  # 无敌传参-->形参,聚合
            print('开外挂')  # 在原有函数前面加新功能
            f = fn(*args, **kwargs)  # 这里是fn函数的调用(-->实参,打散),所以fn函数的返回值被这里接收
            print('关闭外挂')  # 在原有函数后面加新功能
            return f
        return inner
    
    
    def play(username, password):  # 对原有函数完全没有修改
        print('登录:', username, password)
        print('双击LOL')
        print('选择狂战士')
        print('进草丛')
        print('狮子看')
        print('崩山击')
        return '十字斩刀者'
    
    
    def xiaoxiaole(qq):
        print('登录QQ账号:', qq)
        print('消消乐')
        return '奖励10000分'
    
    
    play = wrapper(play)  # 这个需要的时候写上,不需要的时候注释掉
    ret = play('alex', '123')  # 对原有的调用
    print(ret)
    xiaoxiaole = wrapper(xiaoxiaole)
    re = xiaoxiaole(11010)
    print(re)
    装饰器--初级版带参数和返回值
    # 通用装饰器写法 --> Python里面的动态代理
    # # 装饰器存在的意义:在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能(与不使用装饰器之前相比,使用装饰器后,原有目标函数的定义完全不改动,调用完全不改动,执行之后的效果除了增加新的功能也完全不改动)
    def wrapper(fn):  # fn是目标函数
        def inner(*args, **kwargs):  # 无敌形参-->为了目标函数的传参
            '''在执行目标函数之前...'''
            ret = fn(*args, **kwargs)  # 调用目标函数,ret是目标函数的返回值
            '''在执行目标函数之后...'''
            return ret  # 把目标函数返回值返回,保证函数正常的结束(不改变目标函数原有的调用方式还能实现想要的功能)
    
        return inner
    
    
    @wrapper  # 加在目标函数正上方,相当于 target_func = wrapper(target_func)
    def target_func():
        pass
    
    
    # target_func = wrapper(target_func)  # 此时target_func就是fn  写了@wrapper就不需要这句话了
    target_func()  # 此时执行的是inner
    装饰器--通用写法(语法糖)
    from functools import wraps # 引入函数模块
    # 装饰器: 对传递进来的函数进⾏包装. 可以在目标函数之前和之后添加任意的功能. def wrapper(func):
        @wraps(func) # 加在最内层函数最上方,使用函数原来的名字 
        def inner(*args, **kwargs):
            '''在执⾏目标函数之前要执⾏的内容''' 
            ret = func(*args, **kwargs) 
            '''在执⾏目标函数之后要执⾏的内容''' 
            return ret
        return inner
    
    
    # @wrapper 相当于 target_func = wrapper(target_func) 语法糖 
    @wrapper  # 加在目标函数最上方
    def target_func():    
         print("我是目标函数")
    
    
    # 调⽤⽬标函数
    target_func() 
    print(target_func.__name__)  # 不再是inner,而是target_func了
    装饰器--通用写法(wraps版)
    def wrapper1(fn):
        def inner(*args, **kwargs):
            print("我是wrapper1前")
            ret = fn(*args, **kwargs)
            print("我是wrapper1后")
            return ret
        return inner
    
    def wrapper2(fn):
        def inner(*args, **kwargs):
            print("我是wrapper2前")
            ret = fn(*args, **kwargs)
            print("我是wrapper2后")
            return ret
        return inner
    
    def wrapper3(fn):
        def inner(*args, **kwargs):
            print("我是wrapper3前")
            ret = fn(*args, **kwargs)
            print("我是wrapper3后")
            return ret
        return inner
    
    
    # 就近原则
    @wrapper1
    @wrapper2
    @wrapper3
    def func():
        print("我是可怜的func")
    
    func()
    # 就近原则: 1 2 3  func 3 2 1
    >>>
    我是wrapper1前
    我是wrapper2前
    我是wrapper3前
    我是可怜的func
    我是wrapper3后
    我是wrapper2后
    我是wrapper1后
    
    解析:
    执⾏顺序: 首先@wrapper3装饰起来. 然后获取到一个新函数是wrapper3中的inner. 然后执行@wrapper2.这个时候,wrapper2装饰的就是wrapper3中的inner了...以此类推. 所以. 执⾏顺序就像: 第一层装饰器前(第二层装饰器前(第三层装饰器前(目标)第三层装饰器后)第二层装饰器后)第一层装饰器后. 程序从左到右执⾏起来,就是我们看到的结果.
    同一个函数被多个装饰器装饰
    # 函数名+():代表函数的执行  --> 这里返回的结果是一个函数名
    # 装饰器:结构是 @+函数名--->接收  函数名+() 的返回结果 一个函数名
    # @wrapper_out(True)  执行顺序为先计算函数调用的结果,拿结果作为装饰器 -->等价于后面函数的调用加了一个小括号 @(wrapper_out(True)) 这里之所以传进去实参也是因为这里是函数wrapper_out的调用
    def wrapper_out(flag):  # 装饰器本身的函数
        def wrapper(fn):  # 目标函数
            def inner(*args, **kwargs):  # 无敌形参-->为了目标函数的传参
                if flag == True:
                    '''在执行目标函数之前...'''
                    ret = fn(*args, **kwargs)  # 调用目标函数,ret是目标函数的返回值
                    '''在执行目标函数之后...'''
                    return ret  # 把目标函数返回值返回,保证函数正常的结束(不改变目标函数原有的调用方式还能实现想要的功能)
                else:
                    ret = fn(*args, **kwargs)
                    return ret
            return inner
        return wrapper
    
    # 语法糖 @
    @wrapper_out(True)  #加在目标函数正上方,相当于 target_func = wrapper(target_func)
    def target_func():
        pass
    
    
    # 解析:
    注意: 咱们之前的写法是@wrapper 其中wrapper是一个函数(名). 那么也就是说,如果我能让wrapper这⾥换成个函数(名)就⾏了. wrapper_out(True)返回的结果是wrapper也是一个函数啊. 刚刚好和前面的@组合成一个@wrapper. 依然还是原来那个装饰器. 只不过这⾥里套了3层. 但你要能看懂. 其实还是原来那个装饰器@wrappe.
    执⾏步骤: 先执⾏wrapper(True),然后再@返回值,返回值恰好是wrapper,结果就是@wrapper.
    带参数的装饰器

      装饰器的应用:

    '''
    员工星系表框架:
    要求先登录,之后才能进行增删改查操作.(在增,删,改,查的每个函数上方添加登录验证的装饰器)
    '''
    menu = ("查看", "添加", "修改", "删除", "退出")
    
    flag = False  # 没登录
    
    
    def login():
        global flag
        username = input("请输入用户名:")
        password = input("请输入密码:")
        if username == "alex" and password == "123":
            flag = True
            print("登录")
        else:
            flag = False
            print("用户名密码错误")
    
    
    # 登录验证装饰器
    def login_verify(fn):
        def inner(*args, **kwargs):
            # 登录校验
            while 1:
                if flag == True:
                    ret = fn(*args, **kwargs)
                    return ret
                else:
                    print('对不起, 您还没有登录')
                    login()
    
        return inner
    
    
    def chakan():
        print("==============================查看")
    
    
    @login_verify
    def tianjia():
        print("============================添加")
    
    
    @login_verify
    def xiugai():
        print("=======================修改")
    
    
    @login_verify
    def shanchu():
        print("=========================删除")
    
    
    while 1:
        for i in range(len(menu)):
            print(i + 1, menu[i])
    
        num = input("请输入你要执行的菜单:")
        if num == "1":
            chakan()
        elif num == "2":
            tianjia()
        elif num == "3":
            xiugai()
        elif num == "4":
            shanchu()
        elif num == "5":
            print("程序退出中..........")
            exit()
        else:
            print("输入有误. 请重新选择!")

    小结:

    1.开闭原则:
      对功能的扩展是开放的
      对代码的修改是封闭的

    2.通用装饰器语法(必须记住):

    # 装饰器: 对传递进来的函数进⾏包装. 可以在目标函数之前和之后添加任意的功能.
    def wrapper(fn): #  fn是目标函数.
        def inner(*args, **kwargs): # 为了目标函数的传参(聚合)
            '''在执行目标函数之前要执行的内容'''
            ret = fn(*args, **kwargs) # 调用目标函数(打散), ret是目标函数的返回值
            '''在执行目标函数之后要执行的内容'''
            return ret  # 把目标函数返回值返回. 保证函数正常的结束
        return inner
    
    @wrapper  # target_func = wrapper(target_func)
    def target_func():
        pass
    
    # 调用目标函数
    target_func() # 此时执行的是inner

    3.同一个函数被多个装饰器装饰(就近原则)

     @wrapper1
    @wrapper2
    @wrapper3
    def func():
    pass
    执行的结果:1 2 3 func 3 2 1(目标函数被离得近的装饰器括起来)

    4.带参数的装饰器

    def wrapper_out(参数):  # 装饰器本身的参数
          def wrapper(fn):  # fn是目标函数
                 def inner(*args, **kwargs): # 目标函数执行需要的参数-->聚合
                        '''在目标函数之前需要执行的内容'''
                        ret = fn(*arg, **kwargs) # 调用目标函数,ret是目标函数的返回值-->打散
                        '''在目标函数之后需要执行的内容'''
                        return ret  # 把目标函数返回值返回,保证函数以原有的调用方式正常的结束
                 return inner
          return wrapper
    
    @wrapper_out(实参)  # 执行的时候. 先执行函数的调用然后使用返回值和前面的@组合成装饰器语法糖
    def func():  # 目标函数的定义
          pass
    
    func()  # 函数的调用
    
    
  • 相关阅读:
    PHP 如何安全的使用 MySQL ?
    IntelliJ IDEA 内存优化最佳实践
    当我们在谈论 DevOps,我们在谈论什么?
    第84节:Java中的网络编程(中)
    Java工程师必备
    Java工程师必备
    第83节:Java中的学生管理系统分页功能
    第83节:Java中的学生管理系统分页功能
    第82节:Java中的学生管理系统
    第82节:Java中的学生管理系统
  • 原文地址:https://www.cnblogs.com/lyfstorm/p/10119891.html
Copyright © 2011-2022 走看看