7.装饰器

装饰器

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

   开放:多功能扩展开放

   封闭:对修改代码封闭

接下来我们来看一下装饰器,首先我们模拟一下女娲造人

def creat_people():
    print("女娲很厉害,捏一个泥人吹一口气就成了人")
creat_people()

 ok! 很简单. 但是现在问题来了. 上古时期啊. 天气很不稳定. 这时候呢大旱三年. 女娲再去造人啊就很困难了. 因为啥呢? 没水. 也就是说. 女娲想造人必须得先和泥. 浇点儿水才能造人.  

def creat_people():
    print("浇水")
    print("女娲很厉害,捏一个泥人吹一口气就成了人")
creat_people()

   搞定. 但是, 我们来想.是不是违背了我们最开始的那个约定"开闭原则",  我们是添加了 新的功能. 对添加功能开放. 但是修改了源代码啊. 这个就不好了. 因为开闭原则对修改是封闭的. 那怎么办.我们可以这样做 

def creat_people():
    print("女娲很厉害,捏一个泥人吹一口气就成了人")
def warter():
    print("先浇水")
    creat_people()
warter()

   现在问题又来了. 你这个函数写好了. 但是由于你添加了功能.  重新创建了个函数. 在这之前访问过这个函数的人就必须要修改代码来访问新的函数water() 这也要修改代码. 这个也不好. 依然违背开闭原则. 而且. 如果你这个函数被大量的人访问过. 你让他们所有人都去改. 那你就要倒霉了. 不干死你就见鬼了.  那怎么办才能既不修改原代码, 又能添加新功能呢? 这个时候我们就需要一个装饰器了. 装饰器的作用就是在不修改原有代码的基础上, 给函数扩展功能.   

def creat_people():
    print("女娲很厉害,捏一个泥人吹一口气就成了人")
def warter(fn):
    def inner():
        print("先浇水")
        fn()
        print("施肥")
    return inner
creat_people = warter(creat_people)
creat_people()

 1.先访问warter(create_people)

    2.把你的目标函数传递给warter的形参fn,那么后面如果执行了fn意味着执行你的目标函creat_people 

    3.warter()执行就一句话,返回inner函数,这个时候,程序认为warter()函数执行完,哪么前面的creat_pople函数名被重新覆盖成inner函数

    4.执行create_people函数,实际上执行了inner函数,而inner中访问的恰恰使我们最开始传递进取的原生的create_people函数

结论:我们使用warter函数把create_people给包装一下,在不修改creat_people的前提下完成create_people函数的功能添加

这是装饰器的雏形接下来我们观察一下代码,很不要理解,所以我们可以使用一下语法糖来简化我们的代码

def warter(fn):
    def inner():
        print("先浇水")
        fn()
        print("施肥")
    return inner
@warter
def creat_people():
    print("女娲很厉害,捏一个泥人吹一口气就成了人")
creat_people()
结果:
先浇水
女娲很厉害,捏一个泥人吹一口气就成了人
施肥

我们发现代码运行的结果是一样的,所谓的语法糖:@装饰器

类似的操作在我们生活中还有很多,比方说,约一约  

def wrapper(fn):
    def inner():
        print("问金井老板,行情怎么样")
        fn()
        print("金老板骗我,恨你")
    return inner
@wrapper
def yue():
    print("约一约")
yue()

 ok, 接下来. 我们来看一下, 我约的话, 我想约个人. 比如约wusir, 这时, 我们要给函数添加 一个参数  

def wrapper(fn):
    def inner():
        print("问金井老板,行情怎么样")
        fn()
        print("金老板骗我,恨你")
    return inner
@wrapper
def yue(name):
    print("约一约",name)
yue("wusir")
结果:
Traceback (most recent call last):
  File "D:/s18/day15装饰器/__init__.py", line 38, in <module>
    yue("wusir")
TypeError: inner() takes 0 positional arguments but 1 was given

程序报错的原因:我们在外访问yue()的时候,实际上是访问inner函数,而inner函数没有参数,我们给了个参数,这肯定报错,怎么办,我们在inner上加参数就好了  

def wrapper(fn):
    def inner(name):
        print("问金井老板,行情怎么样")
        fn(name)
        print("金老板骗我,恨你")
    return inner
@wrapper
def yue(name):
    print("约一约",name)
yue("wusir")

这样就够了么? 如果我的yue()改成两个参数呢? 你是不是还要改inner. 对了. 用*args和**kwargs来搞定多个参数的问题

def wrapper(fn):
    def inner(*args,**kwargs): #聚合
        print("问金井老板,行情怎么样")
        fn(*args,**kwargs) #解散
        print("金老板骗我,恨你")
    return inner
@wrapper
def yue(name):
    print("约一约",name)
yue("wusir")

  搞定.  这时 wrapper()函数就是一个可以处理带参数的函数的装饰器光有参数还不够. 那返回值呢?

def wrapper(fn):
    def inner(*args,**kwargs): #聚合
        print("问金井老板,行情怎么样")
        ret = fn(*args,**kwargs) #解散
        print("金老板骗我,恨你")
        return ret #把返回值给调用者
    return inner
@wrapper
def yue(name):
    print("约一约",name)
    return "小萝莉"
r = yue("wusir") #这里接收到的返回值是inner返回,inner的函数值是目标函数的返回值
print(r)

 返回值和参数我们搞定了,接下来给出的装饰器的完整模型代码(必须记住) 

#装饰器:对传递进来的函数进行包装,可以在目标函数之前和之后添加任意功能
def wrapper(func):
    def inner(*args,**kwargs):
        '''执行目标函数之前的代码'''
        ret = func(*args,**kwargs)
        """执行目标函数之后的代码"""
        return ret
    return inner
@wrapper
def func():
    print("我是目标函数")
func() #调用目标函数

  我们虽然访问的是func函数. 但是实际上执行的是inner函数. 这样就会给下游的程序员带来困惑. 之前不是一直执行的是func么. 为什么突然换成了inner.  inner是个什么鬼? 为了不让下游程序员有这样的困惑. 我们需要把函数名修改一下. 具体修改方案:   

from functools import wraps
def wrapper(func):
    @wraps(func)
    def inner(*args,**kwargs):
        '''执行目标函数之前的代码'''
        ret = func(*args,**kwargs)
        """执行目标函数之后的代码"""
        return ret
    return inner
#@wrapper 相当于 func = wrapper(func)
@wrapper
def func():
    print("我是目标函数")
func()
print(func.__name__) #不在是inner,而是func了

@wrapper
def new_func():
    print("我是另外一个目标函数")
new_func()
print(new_func.__name__) #new_func

装饰器传参

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():
    print("走起,约去")
yue()

多个装饰器修饰一个函数

def wapper1(fn):
    def inner(*agrs,**kwargs):
        print("111")
        ret = fn(*agrs,**kwargs)
        print("222")
        return ret
    return inner
def wapper2(fn):
    def inner(*agrs,**kwargs):
        print("333")
        ret = fn(*agrs,**kwargs)
        print("444")
        return ret
    return inner
def wapper3(fn):
    def inner(*agrs,**kwargs):
        print("555")
        ret = fn(*agrs,**kwargs)
        print("666")
        return ret
    return inner
@wapper3
@wapper2
@wapper1
def eat(): #就近原则  3 2 1 eat 1 2 3
    print("我想吃水果")

eat()
结果:
555
333
111
我想吃水果
222
444
666

  

 

 

posted @ 2019-02-24 14:30  等待の喵  阅读(308)  评论(0编辑  收藏  举报