zoukankan      html  css  js  c++  java
  • python嵌套函数、闭包与decorator

    1

    一段代码的执行结果不光取决与代码中的符号,更多地是取决于代码中符号的意义,而运行时的意义是由名字空间决定的。名字空间是在运行时由python虚拟机动态维护的,但是有时候我们希望能将名字空间静态化。即:我们希望有的代码不受名字空间变换的影响,始终保持一致的行为和结果。

    这样的意义何在呢?

    这就不得不说说嵌套函数了。

    上面代码中,我们只设置了一次基准值。此后,在每次进行比较操作的时候,尽管调用的实际函数real_compare的local名字空间中没有base,而global名字空间中有base = 1,但是函数调用结果显示,real_compare以一种神奇的方式得知了base应该是10,而不是1。

    也就是说在real_compare函数作为返回值被传递给compare_with_10的时候,有一个名字空间已经与real_compare紧紧地绑定在一起了,在执行real_compare的时候,这个名字空间又被恢复了,这就是一种将名字空间静态化的方法。

    这个名字空间和函数捆绑后的结果被称为一个闭包(closure)。

    比如PyFunctionObject是Python虚拟机专门为字节码指令准备的大包袱,global名字空间、默认参数都能在PyFunctionObject中与字节码指令捆绑在一起,所以PyFunctionObject也是一个Python中闭包的具体表现。

    闭包是最内嵌套的一种实现。

    不用闭包我们也能实现上面相同的功能,

    上面这个就不是闭包,为什么呢?

    因为最后一个print语句中传入的base=1的情况,居然能够改变基准值,这里如果是闭包的话,那就会抛出异常的。

    我们利用函数默认值,居然实现了闭包的效果。

    那么,闭包和默认参数的实现方式是不是相似的呢?

    1.1实现闭包的基石

    闭包的创建通常是利用嵌套函数来完成的。在PyCodeObject中,与嵌套函数相关的属性有co_cellvars 、co_freevars。前者是一个保存嵌套的作用域中使用的变量的集合,后者是保存使用了外层作用区域中的变量的集合,两者均为tuple。

    PyFrameObject对象中,也有与闭包实现相关的属性,这就是f_localsplus,在PyFrame_New中,extras = code->co_stacksize + code->co_nlocals+ncells+ nfrees;

    extras正是f_localsplus指向的那片内存的大小。

    PyFunctionObject中,还有一个与闭包实现有关的属性。

    1.2闭包实现

    1.2.1创建closure

    在PyCodeObject的co_cellvars中有东西,在PyEval_EvalCodeEx中,Python虚拟机就会如同处理默认参数一样,将co_cellvars中的东西拷贝到新创建的PyFrameObject的f_localsplus中。

    python虚拟机先获得被内层嵌套函数引用的符号名,然后就创建一个cell对象,随后cell对象被拷贝到新创建的PyFrameObject的f_localsplus中。位置为co->co_nlocals+i,说明在f_localsplus中,cell对象的位置是在局部变量之后的。

    上面总结就是:把PyCodeObject中的co_cellvars写道PyFrameObject的f_localsplus.

    处理了cell对象之后,Python虚拟机进入PyEval_EvalFrameEx,从而正式开始函数调用。

    从PyFrame中取得f_localsplus中的cell对象,存为freevars对象。在PyFunctionObject中,存储需要传递的内容。

    2.2使用closure

    闭包是在外层函数中创建,在内层函数中使用。

    在将内层函数赋值给一个名字之后,例如show_value,

    然后在show_vale()的时候,发现内层函数对应的PyCodeObject中的co_flags中包含了CO_NESTED,所以会进入到PyEval_EvalCodeEx.

    而在内层函数对应的PyCodeObject中co_freevars里有引用外层函数中的符号名字,在PyEval_EvalCodeEx中,就会对这个co_freevars进行处理。

    即在PyFunctionObject对象中与PyCodeObject对象绑定的装满了PyCellObject对象的tuple,所以这里其实偶是将PyCellObject对象一个一个地放入到f_localsplus中相应的位置。

    处理完之后,PyFrameObject中就有闭包需要的元素了。

    这里和调用外层函数时一致的,在内层函数调用的过程中,当引用外层作用域符号时,一定是到f_localsplus中的free变量区域中获取得到符号对应的值。

    到这里我们已经讲完closure的创建到传递到使用的全过程了。

    2.3 decorator

    装饰器的本质其实就是闭包。

    其他没啥好说的了,只是包装了一下而已。

  • 相关阅读:
    8天学通MongoDB——第五天 主从复制
    5天不再惧怕多线程——第五天 线程池
    8天玩转并行开发——第四天 同步机制(上)
    8天学通MongoDB——第八天 驱动实践
    8天玩转并行开发——第三天 plinq的使用
    8天玩转并行开发——第一天 Parallel的使用
    8天玩转并行开发——第五天 同步机制(下)
    5天不再惧怕多线程——第一天 尝试Thread
    虚函数、纯虚函数详解
    libevent 笔记
  • 原文地址:https://www.cnblogs.com/hackerl/p/5985102.html
Copyright © 2011-2022 走看看