python 装饰函数 和 闭包 基础总结。

系统 1577 0

一、 装饰函数

(1)概念:

装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下 增加额外功能 ,装饰器的返回值也是一个 函数/类对象 。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是 为已经存在的对象添加额外的功能

使用方法:

  1. 先定义一个装饰器(帽子)
  2. 再定义你的业务函数或者类(人)
  3. 最后把这装饰器(帽子)扣在这个函数(人)头上

(2)实例:

  1. 日志打印器
            
              
                # 这是装饰器函数,参数 func 是被装饰的函数
              
              
                def
              
              
                logger
              
              
                (
              
              func
              
                )
              
              
                :
              
              
                def
              
              
                wrapper
              
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kw
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                '主人,我准备开始执行:{} 函数了:'
              
              
                .
              
              
                format
              
              
                (
              
              func
              
                .
              
              __name__
              
                )
              
              
                )
              
              
                # 真正执行的是这行。
              
              
        func
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kw
              
                )
              
              
                print
              
              
                (
              
              
                '主人,我执行完啦。'
              
              
                )
              
              
                return
              
               wrapper
@logger  
              
                # =》 add = logger(add)
              
              
                def
              
              
                add
              
              
                (
              
              x
              
                ,
              
               y
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                '{} + {} = {}'
              
              
                .
              
              
                format
              
              
                (
              
              x
              
                ,
              
               y
              
                ,
              
               x
              
                +
              
              y
              
                )
              
              
                )
              
            
          

执行:
在这里插入图片描述
2. 时间计时器

            
              
                # 这是装饰函数
              
              
                def
              
              
                timer
              
              
                (
              
              func
              
                )
              
              
                :
              
              
                def
              
              
                wrapper
              
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kw
              
                )
              
              
                :
              
              
        t1
              
                =
              
              time
              
                .
              
              time
              
                (
              
              
                )
              
              
                # 这是函数真正执行的地方
              
              
        func
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kw
              
                )
              
              
        t2
              
                =
              
              time
              
                .
              
              time
              
                (
              
              
                )
              
              
                # 计算下时长
              
              
        cost_time 
              
                =
              
               t2
              
                -
              
              t1 
        
              
                print
              
              
                (
              
              
                "花费时间:{}秒"
              
              
                .
              
              
                format
              
              
                (
              
              cost_time
              
                )
              
              
                )
              
              
                return
              
               wrapper

              
                import
              
               time

@timer

              
                def
              
              
                want_sleep
              
              
                (
              
              sleep_time
              
                )
              
              
                :
              
              
    time
              
                .
              
              sleep
              
                (
              
              sleep_time
              
                )
              
              

want_sleep
              
                (
              
              
                10
              
              
                )
              
              
                #花费时间:10.000298261642456秒
              
            
          
  1. 带参数的函数装饰器
            
              
                def
              
              
                say_hello
              
              
                (
              
              contry
              
                )
              
              
                :
              
              
                def
              
              
                wrapper
              
              
                (
              
              func
              
                )
              
              
                :
              
              
                def
              
              
                deco
              
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kwargs
              
                )
              
              
                :
              
              
                if
              
               contry 
              
                ==
              
              
                "china"
              
              
                :
              
              
                print
              
              
                (
              
              
                "你好!"
              
              
                )
              
              
                elif
              
               contry 
              
                ==
              
              
                "america"
              
              
                :
              
              
                print
              
              
                (
              
              
                'hello.'
              
              
                )
              
              
                else
              
              
                :
              
              
                return
              
              
                # 真正执行函数的地方
              
              
            func
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kwargs
              
                )
              
              
                return
              
               deco
    
              
                return
              
               wrapper


              
                # 小明,中国人
              
              
@say_hello
              
                (
              
              
                "china"
              
              
                )
              
              
                def
              
              
                xiaoming
              
              
                (
              
              
                )
              
              
                :
              
              
                pass
              
              
                # jack,美国人
              
              
@say_hello
              
                (
              
              
                "america"
              
              
                )
              
              
                def
              
              
                jack
              
              
                (
              
              
                )
              
              
                :
              
              
                pass
              
            
          

python 装饰函数 和 闭包 基础总结。_第1张图片
4. 不带参数的类装饰器
基于类装饰器的实现,必须实现 call 和 __init__两个内置函数。
init :接收被装饰函数
call :实现装饰逻辑。

            
              
                class
              
              
                logger
              
              
                (
              
              
                object
              
              
                )
              
              
                :
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
               func
              
                )
              
              
                :
              
              
        self
              
                .
              
              func 
              
                =
              
               func

    
              
                def
              
              
                __call__
              
              
                (
              
              self
              
                ,
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kwargs
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                "[INFO]: the function {func}() is running..."
              
              \
            
              
                .
              
              
                format
              
              
                (
              
              func
              
                =
              
              self
              
                .
              
              func
              
                .
              
              __name__
              
                )
              
              
                )
              
              
                return
              
               self
              
                .
              
              func
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kwargs
              
                )
              
              

@logger

              
                def
              
              
                say
              
              
                (
              
              something
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                "say {}!"
              
              
                .
              
              
                format
              
              
                (
              
              something
              
                )
              
              
                )
              
              

say
              
                (
              
              
                "hello"
              
              
                )
              
              
                #[INFO]: the function say() is running...
              
              
                #say hello!
              
            
          
  1. 带参数的类装饰器
    上面不带参数的例子,你发现没有,只能打印INFO级别的日志,正常情况下,我们还需要打印DEBUG WARNING等级别的日志。这就需要给类装饰器传入参数,给这个函数指定级别了。

带参数和不带参数的类装饰器有很大的不同。

init :不再接收被装饰函数,而是接收传入参数。
call :接收被装饰函数,实现装饰逻辑。

            
              
                class
              
              
                logger
              
              
                (
              
              
                object
              
              
                )
              
              
                :
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
               level
              
                =
              
              
                'INFO'
              
              
                )
              
              
                :
              
              
        self
              
                .
              
              level 
              
                =
              
               level

    
              
                def
              
              
                __call__
              
              
                (
              
              self
              
                ,
              
               func
              
                )
              
              
                :
              
              
                # 接受函数
              
              
                def
              
              
                wrapper
              
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kwargs
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                "[{level}]: the function {func}() is running..."
              
              \
                
              
                .
              
              
                format
              
              
                (
              
              level
              
                =
              
              self
              
                .
              
              level
              
                ,
              
               func
              
                =
              
              func
              
                .
              
              __name__
              
                )
              
              
                )
              
              
            func
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kwargs
              
                )
              
              
                return
              
               wrapper  
              
                #返回函数
              
              

@logger
              
                (
              
              level
              
                =
              
              
                'WARNING'
              
              
                )
              
              
                def
              
              
                say
              
              
                (
              
              something
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                "say {}!"
              
              
                .
              
              
                format
              
              
                (
              
              something
              
                )
              
              
                )
              
              

say
              
                (
              
              
                "hello"
              
              
                )
              
              
                #[WARNING]: the function say() is running...
              
              
                #say hello!
              
            
          
  1. 内置装饰器:property
            
            
          
  1. 如果 decorator本身需要传入参数 ,那就需要编写一个返回decorator的高阶函数。(即再嵌套一个decorator函数):
            
              
                def
              
              
                log
              
              
                (
              
              text
              
                )
              
              
                :
              
              
                def
              
              
                decorator
              
              
                (
              
              func
              
                )
              
              
                :
              
              
        @functools
              
                .
              
              wraps
              
                (
              
              func
              
                )
              
              
                #把原始函数的__name__等属性复制到wrapper()函数中
              
              
                def
              
              
                wrapper
              
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kw
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                '%s %s():'
              
              
                %
              
              
                (
              
              text
              
                ,
              
               func
              
                .
              
              __name__
              
                )
              
              
                )
              
              
                return
              
               func
              
                (
              
              
                *
              
              args
              
                ,
              
              
                **
              
              kw
              
                )
              
              
                return
              
               wrapper
    
              
                return
              
               decorator

@log
              
                (
              
              
                'execute'
              
              
                )
              
              
                #now = log('execute')(now)
              
              
                def
              
              
                now
              
              
                (
              
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                '2015-3-25'
              
              
                )
              
              

now
              
                (
              
              
                )
              
              
                #execute now():
              
              
                #2015-3-25
              
            
          

首先执行log(‘execute’),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

二、闭包

(1)概念:

在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包。
维基百科上的解释是:

              
                在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,
是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,
即使已经离开了创造它的环境也不例外。
所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。

              
            

在这里插入图片描述

add访问了外部函数start的变量,并且函数返回值为add函数(python可以 返回函数
python 装饰函数 和 闭包 基础总结。_第2张图片
闭包,顾名思义,就是一个封闭的包裹,里面包裹着 自由变量 ,就像在类里面定义的属性值一样,自由变量的可见范围随同包裹,哪里可以访问到这个包裹,哪里就可以访问到这个自由变量。

再通过Python的语言介绍一下,一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量(当函数A的生命周期结束之后,自由变量依然存在,因为它被闭包引用了,所以不会被回收。)。

(2)常见问题

  1. 闭包无法修改 外部函数 局部变量 (即add函数无法修改start函数定义的变量)

  2. 闭包使得 局部变量 函数外 被访问成为可能

  3. 闭包避免了使用全局变量

  4. 闭包允许将函数与其所操作的某些数据(环境)关连起来。

  5. 装饰器就是一种的闭包的应用,只不过其传递的是 函数

  6. 闭包的最大特点是可以将 父函数的变量与内部函数绑定 ,并返回绑定变量后的函数(也即闭包)。(类似类)

  7. python循环中不包含域的概念。

python 装饰函数 和 闭包 基础总结。_第3张图片
loop在python中是没有域的概念的,flist在向列表中添加func的时候,并没有保存i的值,而是当执行f(2)的时候才去取,这时候循环已经结束,i的值是2,所以结果都是4。

解决办法:
在func外面再定义一个makefun函数,func形成闭包。 python 装饰函数 和 闭包 基础总结。_第4张图片
参考:
https://foofish.net/python-closure.html
https://zhuanlan.zhihu.com/p/22229197


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论