zoukankan      html  css  js  c++  java
  • (十四)装饰器

    一、说明

       装饰器,顾名思义就是对某个东西进行化妆,让它更好看一些,在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三大器:迭代器,生成器,装饰器。值得好好学习一下。

  • 相关阅读:
    一个简单而实用的JQ插件——lazyload.js图片延迟加载插件
    CSS预处理语言——less与sass的使用
    JQuery速成大法
    实现图片的循环滚动——JS的简单应用
    JS基础——循环很重要
    JS基础——入门必备
    做一个常规的banner图——负边距的使用、banner图的拼法
    网页侧边浮动条的实现
    如何做一个导航栏————浮动跟伪类(hover)事件的应用
    基于java代码的springmvc配置
  • 原文地址:https://www.cnblogs.com/asia-yang/p/10121735.html
Copyright © 2011-2022 走看看