最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 深入理解Python中的闭包与装饰器

    正文概述    2020-04-05   141

    深入理解Python中的闭包与装饰器

    函数的装饰器可以以某种方式增强函数的功能,如在 Flask 中可使用 @app.route('/') 为视图函数添加路由,是一种十分强大的功能。在表现形式上,函数装饰器为一种嵌套函数,这其中会涉及到闭包的概念。而在嵌套函数之间,外部函数中的变量相对于内部函数而言为自由变量,使用时可能需要借助于 nonlocal 关键字进行声明。

    nonlocal 声明

    按变量的作用域进行分类,Python 中的变量可分为「全局变量」、「局部变量」以及「自由变量」。一般而言,Python 中使用变量前不需要声明变量,但假定在函数体中赋值的变量为局部变量~除非显示使用 global 将在函数中赋值的变量声明为全局变量!

    而自由变量则是存在于嵌套函数中的一个概念~定义在其他函数内部的函数被称之为嵌套函数 nested function ,嵌套函数可以访问封闭范围内(外部函数)的变量。嵌套函数不可以在函数外直接访问。

    在 Python 中,非本地变量默认仅可读取,在修改时必须显式指出其为非本地变量~自由变量 nonlocal,全局变量 global。

    >>> ga = 1
    >>> def func():
    ...     nb = 2
    ...     def inner():
    ...         ga += 1
    ...         nb += 2
    ...         print('ga is %s, and nb is %s' % (ga, nb))
    ...     return inner
    ...
    >>> test = func()
    Traceback (most recent call last):
    ...
    UnboundLocalError: local variable 'ga' referenced before assignment

    未加入全局变量和自由变量声明时且使用赋值操作时,inner 函数的变量 ga, nb 默认为局部变量,会报错;如注释掉 ga += 1 后同样会报错:

    Traceback (most recent call last):
    ...
    UnboundLocalError: local variable 'nb' referenced before assignment

    可行改写如下:

    >>> ga = 1
    >>> def func():
    ...     nb = 2
    ...     def inner():
    ...         global ga
    ...         nonlocal nb
    ...         ga += 1
    ...         nb += 2
    ...         print('ga is %s, and nb is %s' % (ga, nb))
    ...     return inner
    ...
    >>> test = func()
    >>> test()
    ga is 2, and nb is 4
    >>> test()
    ga is 3, and nb is 6

    通过显示声明 ga, nb 分别为「全局变量」和「自由变量」,此时如预期运行!

    闭包

    函数内的函数以及其自由变量形成闭包。也即闭包是一种保留定义函数时存在的自由变量的绑定的函数~这样在调用函数时,绑定的自由变量依旧可用。

    闭包可以避免全局变量的使用以及提供某种形式的数据隐藏。当函数中的变量和函数较少且其中某个功能常用时,使用闭包来进行封装。当变量和函数更加复杂时,则使用类来实现。

    # 计算移动平均值的函数
    def make_averager():
        series = []
    
        def averager(new_value):
            series.append(new_value)
            total = sum(series)
            return total/len(series)
    
        return averager

    那么此时,make_averager() 函数的第二行 series = [] 到第七行 return total/len(series) 为闭包,变量 series 为 averager() 函数中的自由变量!

    # avg 为一个 averager 函数对象 ~ 含自由变量的绑定
    >>> avg = make_averager()
    >>> avg(10)
    10.0
    >>> avg(11)
    10.5
    >>> avg(12)
    11
    # 创建另一个 averager 函数对象
    >>> avg2 = make_averager()
    >>> avg2(1)
    1.0
    >>> avg2(18)
    9.5
    # 查看 avg, avg2 自由变量中保存的值
    >>> avg.__closure__[0].cell_contents
    [10, 11, 12]
    >>> avg2.__closure__[0].cell_contents
    [1, 18]

    函数对象通过 __closure__ 属性 —— 返回 cell 对象元祖(函数中有多少嵌套函数则该元祖的长度有多长),生成该对象的函数被称之为闭包函数。

    func.__closure__[0].cell_contents: 访问存储在 cell 对象中值。

    装饰器

    装饰器本身是一个可调用的对象~函数或类,其参数为另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数(如添加一些功能)然后将之返回,或者将之替换为另一个函数或可调用对象。这也被称之为元编程 metaprogramming —— 在编译时改变函数功能。

    >>> def make_pretty(func):
    ...     def inner():
    ...         print("I got decorated!", end='\t')
    ...         func()
    ...     return inner
    ...
    >>> def ordinary():
    ...     print("I am ordinary!")
    
    # 用 make_pretty 函数装饰 ordinary 函数
    >>> pretty = make_pretty(ordinary)
    >>> pretty()
    I got decorated!  I am ordinary!

    可以作为装饰器的函数内部都有嵌套的功能函数(用以实现主要功能),并返回内部的嵌套函数。

    @make_pretty
    def ordinary():
        print("I am ordinary!")
    
    # 等价于
    def ordinary():
        print("I am ordinary!")
    ordianry = make_pretty(ordinary)

    make_pretty(func) 是一个最简单的装饰器,它接受一个函数为其参数;内部定义了一个 inner() 函数~输出 "I got decorated!" 后执行被装饰函数(此时 func 为 inner 闭包中的自由变量);然后返回内部函数 inner。

    此时,对于被装饰的函数 ordinary 而言,此时是 inner 的引用:

    >>> ordinary()
    I got decorated!  I am ordinary!
    >>> ordinary
    <function make_pretty.<locals>.inner at 0x10aeaa1e0>

    除了最简单的装饰器之外,还可以将多个装饰器叠放使用

    叠放装饰器

    def star(func):
        def inner(*args, **kwargs):
            print('*' * 30)
            func(*args, **kwargs)
            print('*' * 30)
        return inner
    
    def dollar(func):
        def inner(*args, **kwargs):
            print('$' * 30)
            func(*args, **kwargs)
            print('$' * 30)
        return inner
    
    @star
    @doller
    def printer(msg):
        print(msg)
    
    printer("Hello world!")
    
    # 结果如下
    '''
    ******************************
    $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    Hello world!
    $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    ******************************
    '''
    
    # 等价于
    def printer(msg):
        print(msg)
    printer = star(dollar(printer))

    起源地下载网 » 深入理解Python中的闭包与装饰器

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元