装饰器 装饰器语法 同一个函数被多个装饰器装饰 带参数的装饰器 三大器完毕(迭代器,生成器,装饰器)

装饰器 

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

    开放: 对功能扩展开放       

    封闭: 对修改代码封闭

 

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

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

###

 

posted @ 2018-12-14 16:33  言吾  阅读(298)  评论(0编辑  收藏  举报