一、说明

   装饰器,顾名思义就是对某个东西进行化妆,让它更好看一些,在Python中装饰器的作用就是对函数进行装饰。那么问题来了,怎么装饰呢?这里就用到了前面介绍函数时,说过的几个点:

  1. 函数名可以赋值给其他变量
  2. 函数名可以作为参数
  3. 函数名可以当做容器类的元素
  4. 函数名可以作为函数的返回值。
  5. 闭包

      以上几点,被使用在装饰器中,所以一定要理解这几点才可以看下面的内容。

二、讲个故事

      我写了一个软件,对这个操作过程进行一下简单的描述:

def openSoft():
    print('打开手机')
    print('找到软件并点击,就打开了')
openSoft()

  

   是不是so easy。好了,大多数软件都会有登录功能。现在,我也要加上,但是我的function(),我不想修改,怎么办呢?直接在“打开手机”前,再写一个“登录”就好了,但是有没有想过,如果只有一处调用,还好改,但是如果有很多处调用了这个openSoft()呢?而且,有的地方,我不需要登录功能,还保持原来的,这样就尴尬了,只能重新写一个函数了。这时就体现出装饰器的作用了。看代码:

 

def openSoft():
    print('打开手机')
    print('找到软件并点击,就打开了')
def wrapper(f):
    def inner():
        print('输入手机密码')
        f()#在这里,调用openSoft()
    return inner#把inner作为返回值,返给调用的地方
ret = wrapper(openSoft)#获取到inner,ret接收到的是inner的内存地址,使用id查看
ret()#加上()后,这里就是调用inner了

 

  

   可以看到的,我没有对openSoft动手,只是又写了一个嵌套的函数。而且也很完美的把,登录功能,添加上了。接下来,又有一个问题了,手机有密码,怎么办呢?最开始使用openSoft()的地方不想改,怎么操作?看代码:

 

def wrapper(f):
    def inner():
        print('输入手机密码')
        f()#在这里,调用openSoft()
    return inner#把inner作为返回值,返回去

def openSoft():
    print('打开手机')
    print('找到软件并点击,就打开了')
openSoft = wrapper(openSoft)#获取到inner
openSoft()#加上()后,这里就是调用inner了

 

  

   看到问题所在了吗?我把ret改成了openSoft,这样openSoft就被重新赋值了,也就代表了新的函数。运行可以看到,与上面ret时,是一样的。代码添加了,不少,本着,越少代码越好的原则,再看一下下面的代码:

 

def wrapper(f):
    def inner():
        print('输入手机密码')
        f()#在这里,调用openSoft()
    return inner#把inner作为返回值,返回去
@wrapper#这里相当于:openSoft = wrapper(openSoft)#获取到inner
def openSoft():
    print('打开手机')
    print('找到软件并点击,就打开了')
openSoft()

 

  

   运行的结果与上面还是一样的,可以看到代码少了一些,看着也简单,但是也出现一个新的东西----@”。这个@,在这里叫做语法糖,就是给装饰器使用的。

完美对的解决了一些问题,接下来,再思考一个问题,软件打开了,我得告诉用户一下,也就是给个返回值。我想从openSoft中返回,告诉用户“恭喜你,软件打开了!”。再看代码:

 

def wrapper(f):
    def inner():
        print('输入手机密码')
        ret = f()#在这里,调用openSoft(),并接收到返回值
        return ret#把openSoft里面的值,在这里继续返回给调用者
    return inner#把inner作为返回值,返回去
@wrapper
def openSoft():
    print('打开手机')
    print('找到软件并点击,就打开了')
return '恭喜你,软件打开了!'
ret = openSoft()
print(ret)

 

  

       Very nice,接收到了。这里想说明一点的是,openSoft返回值,后还得使用inner,再向上一层调用者继续返回一下。也就是,那里调用的,得返给哪里。

现在又有一个问题了,我要知道什么牌子打开了手机而且openSoft中显示“打开了xxx手机”,这个“xxx”是个动态接收的地方。再看代码,在装饰器中怎么传递参数。

 

def wrapper(f):
    def inner(phone_name):
        print('输入手机密码')
        ret = f(phone_name)#在这里,调用openSoft()
        return ret
    return inner#把inner作为返回值,返回去
@wrapper
def openSoft(name):
    print(f'打开{name}手机')
    print('找到软件并点击,就打开了')
    return '恭喜你,软件打开了!'
ret = openSoft("华为")
print(ret)

 

  

   结果又是完美的,“打开了华为手机”。这里对openSoft进行了修改,目的是为了表示出传参的操作。

        总结一下,看看装饰器的结构是什么样的?

# 装饰器: 对传递进来的函数进⾏包装. 可以在⽬标函数之前和之后添加任意的功能.
def wrapper(func):
    def inner(*args, **kwargs):
        '''在执⾏⽬标函数之前要执⾏的内容'''
        ret = func(*args, **kwargs)
        '''在执⾏⽬标函数之后要执⾏的内容'''
        return ret
    return inner
# @wrapper 相当于 target_func = wrapper(target_func) 语法糖
@wrapper
def target_func():
    print("我是⽬标函数")

  

       这是一个超级无敌的模式,好好记住了吧。

      上面的公式记好了,下面再来看一个问题,我的手机如果没有黑屏,一直在玩呢,要想打开这个软件就不需要再次输入密码了。所以,我需要一个开关来控制一下,是否需要输入密码。看代码:

isN = True#控制全局的变量,在这里可以遥控手机是否需要输入密码
def wrapper_outer(isNeed):#在原来的基础上又加了一层
    def wrapper(f):
        def inner(phone_name):
            if isNeed:
                print('输入手机密码')
                ret = f(phone_name)  # 在这里,调用openSoft()
                return ret
            else:
                ret = f(phone_name)  # 在这里,调用openSoft()
                return ret
        return inner#把inner作为返回值,返回去
    return wrapper
@wrapper_outer(isN)#在这里输入,True或者False。wrapper_outer(isN)获得wrapper
def openSoft(name):
    print(f'打开{name}手机')
    print('找到软件并点击,就打开了')
    return '恭喜你,软件打开了!'
ret = openSoft("华为")
print(ret)

  

   咱们之前的写法是@wrapper 其中wrapper是⼀个函数. 那么也就是说. 如果我能让wrapper这⾥换成个函数就⾏了. wrapper(True)返回的结果是wrapper也是⼀个函数啊. 刚刚好和前⾯的@组合成⼀个@wrapper. 依然还是原来那个装饰器. 只不过这⾥套了3. 但你要能看懂. 其实还是原来那个装饰器@wrapper

       上面是套在一起进行装饰,再看下面的摞在一起的。

def wrapper2(fn):
    def inner(*args, **kwargs):
        print("333")
        ret = fn(*args, **kwargs)
        print("444")
        return ret
    return inner

def wrapper1(fn):
    def inner(*args, **kwargs):
        print("111")
        ret = fn(*args, **kwargs)
        print("222")
        return ret
    return inner

@wrapper2
@wrapper1
def eat():
    print("我想吃⽔果")
eat()
结果:
333
111
我想吃⽔果
222
444

  

   执⾏顺序: ⾸先@wrapper1装饰起来. 然后获取到⼀个新函数是wrapper1中的inner. 然后执⾏@wrapper2.这个时候. wrapper2装饰的就是wrapper1中的inner. 所以. 执⾏顺序就像:第⼆层装饰器前(第⼀层装饰器前(⽬标)第⼀层装饰器后)第⼆层装饰器后. 程序从左到右执⾏起来. 就是我们看到的结果

  Python三大器:迭代器,生成器,装饰器。值得好好学习一下。