zoukankan      html  css  js  c++  java
  • 装饰器 装饰器语法 同一个函数被多个装饰器装饰 带参数的装饰器 三大器完毕(迭代器,生成器,装饰器)

    装饰器 

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

        开放: 对功能扩展开放       

        封闭: 对修改代码封闭

    这里定义一个女娲造人的函数

    def zaoren(): # print("浇水") # 此需求有的时候需要. 有的时候不需要 print("捏个泥人") print("吹口仙气") print("你就出来了")

    女娲要造人,直接执行函数就行了
    假设没有水怎么办? ----->>浇水

    def water():
    print("浇水")
    zaoren()

    # 此时的设计就不符合开闭原则

    所以在不改变源代码的前提下,我们可以这样做

    def wrapper(fn): # fn接收的是一个函数
        def inner():
            print("浇水")
            fn() # 调用你传递进来的函数
            print("睡一觉")
        return inner
    
    def zaoren():
        print("捏个泥人")
        print("吹口仙气")
        print("你就出来了")
    
    zaoren = wrapper(zaoren)
    
    zaoren()
    zaoren()

      这里我们就用另外一个装饰器装饰了函数zaoren

      这就是装饰器的作用

       现在问题⼜来了. 你这个函数写好了. 但是由于你添加了功能.  重新创建了个函数. 在这之前访问过这个函数的人就必须要修改代码来访问新的函数water() 这也要修改代码. 这个也不 好. 依然违背开闭原则. ⽽且. 如果你这个函数被⼤量的人访问过. 你让他们所有⼈都去改.那你就要倒霉了. 不干死你就⻅鬼了.     

      那怎么办才能既不修改原代码, 又能添加新功能呢? 这个时候我们就需要⼀个装饰器了. 装饰器的作用就是在不修改原有代码的基础上, 给函数扩展功能. 

    def play(username, password):
        print("双击lol")
        print("登录", username, password)
        print("选择狂战士")
        print("进草丛")
        print("崩山击, 十字斩")
    return "月之光芒"
    # def xiaoxiaole(qq): print("登录qq账号") print("消消乐") # # # 开挂 # # 关闭外挂 # # 在目标函数前和后插入一段新的代码. 不改变原来的代码 def wrapper(fn): # fn = play def inner(*args, **kwargs): # 无敌传参 接受到的是元组 ("alex", 123) print("开挂") ret = fn(*args, **kwargs) # 接受到的所有参数. 打散传递给正常的参数 print("关闭外挂") return "月之光芒" return inner # play = wrapper(play) # play = inner # ret = play('alex',"123") # print(ret) # None ret = play(111,222) #这里的参数无论是几个,都能够顺利的传入,就是运用到的无敌传参 print(ret)

    下面我们总结一下装饰器的通用写法:

    装饰器就是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():  ##这里虽然执行的是target_func实际上还是执行的wrapper
        pass
    
    # target_func = wrapper(target_func) # 此时fn就是target_func
    target_func() # 此时执行的是inner

    这里有个语法糖的运用

    @wrapper        就是 target_func = wrapper(target_func)

    装饰器传参

    from functools import wraps 
    
    def wrapper(fn):    
        @wraps(fn)    
        def inner(*args, **kwargs):        
            print("金⽼板啊, 行情怎么样.")       
            ret = fn(*args, **kwargs)        
            print("⾦老板骗我")       
            return ret    
      return inner @wrapper def yue(): print("约⼀一次⼜又不不会死") yue() 那么现在如果查的很严. 怎么办呢? 打电话问⾦老板严不严.
    那如果整体⻛声都不是那么 紧呢. 是不是就不需要问⾦老板了.
    所以. 我们需要⼀个开关来控制是否要询问金老板.
    这时 我们就需要给装饰器传递⼀一个参数.
    来通知装饰器要⽤怎么样的⽅式来装饰你的目标函

    这里外面又套了一层函数,用来判断是否要用装饰器装饰函数

    from functools import wraps
    
    def wrapper_out(flag):    
        def wrapper(fn):       
            @wraps(fn)        
            def inner(*args, **kwargs):           
                 if flag == True:    # 查的严啊. 先问问吧  
                     print("问问⾦老板啊, ⾏情怎么样.")               
                     ret = fn(*args, **kwargs)                
                     print("⺾艹, ⾦⽼板骗我")               
                     return ret            
                else:   # 查的不不严. 你慌什什么
                     ret = fn(*args, **kwargs)                
                     return ret        
            return inner    
        return wrapper
    @wrapper_out(False)     
    ### 传递True和False来控制装饰器器内部的运⾏行行效果 
    def yue():    
    print("约一次⼜不会死")  
    yue()                    

     注意: 咱们之前的写法是@wrapper    其中wrapper是一个函数. 那么也就是说. 如果我能让 wrapper这里换成个函数就行了. wrapper(True)返回的结果是wrapper也是一个函数啊. 刚刚 好和前⾯面的@组合成一个@wrapper. 依然还是原来那个装饰器. 只不过这里套了3层. 但你要 能看懂. 其实还是原来那个装饰器@wrapper    执行步骤: 先执行wrapper(True) 然后再@返回值.   返回值恰好是wrapper. 结果就是 @wrapper 

    在这里,我写成这样,更容易看懂一些

    def wrapper_out(flag): # 装饰器本身的参数
        def wrapper(fn): # 目标函数
            def inner(*args, **kwargs): # 目标函数执行需要的参数
                if flag == True:
                    print("问问金老板. 行情怎么样啊")
                    ret = fn(*args, **kwargs) # 在执行目标函数之前
                    print("金老板骗我. 恨你")
                    return ret
                else:
                    ret = fn(*args, **kwargs)  # 在执行目标函数之前
                    return ret
            return inner
        return wrapper
    
    
    # 语法糖 @装饰器
    @wrapper_out(True) # 先执行wrapper_out(True) 返回一个装饰器   再和@拼接  @装饰器
    def yue(): # 被 wrapper装饰
        print("走啊. 约不?")
    
    yue()

    假使多个装饰器对同一个函数进行装饰,会是什么效果##就近原则##

    def wrapper1(fn):
        def inner(*args, **kwargs):
            print("1111111")
            ret = fn(*args, **kwargs)
            print("2222222")
            return ret
        return inner
    
    def wrapper2(fn):
        def inner(*args, **kwargs):
            print("3333333")
            ret = fn(*args, **kwargs)
            print("44444444")
            return ret
        return inner
    
    def wrapper3(fn):
        def inner(*args, **kwargs):
            print("555555")
            ret = fn(*args, **kwargs)
            print("666666")
            return ret
        return inner
    
    
    # 就近原则
    @wrapper1
    @wrapper2
    @wrapper3
    def func():
        print("我是可怜的func")
    
    func()
    # 1 2 3  func 3 2 1
    执行顺序: ⾸首先@wrapper1装饰起来. 然后获取到一个新函数是wrapper1中的inner.
    然后执 ⾏@wrapper2.这个时候. wrapper2装饰的就是wrapper1中的inner了了.
    所以. 执行顺序就像: 第⼆层装饰器前(第⼀层装饰器前(目标)第一层装饰器后)第二层装饰器后. 
    程序从左到右执⾏起来. 就是我们看到的结果
    ###

    1111111
    3333333
    555555
    我是可怜的func
    666666
    44444444
    2222222

    ###

  • 相关阅读:
    httpwebrequest详解【转】
    javascript中event.keycode大全
    public void Delete<T>(List<T> EntityList) where T : class, new() 这是什么意思
    Vue(基础五)_vue中用ref和给dom添加事件的特殊情况
    Vue(基础四)_总结五种父子组件之间的通信方式
    Vue(基础三)_监听器与计算属性
    vue(基础二)_组件,过滤器,具名插槽
    vue(基础一)_基本指令的使用
    JS(基础)_总结获取页面中元素和节点的方式
    node.js(node.js+mongoose小案例)_实现简单的注册登录退出
  • 原文地址:https://www.cnblogs.com/H1050676808/p/10120180.html
Copyright © 2011-2022 走看看