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

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

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

python嵌套函数、闭包与decorator的更多相关文章

  1. Python 嵌套函数和闭包

    Python 嵌套函数和闭包 1.函数嵌套 如果在一个函数内部定义了另一个函数,我们称外部的函数为外函数,内部的函数为内函数,如下代码: def out_func(): def inner_func1 ...

  2. python之函数闭包、可迭代对象和迭代器

    一.函数名的应用 # 1,函数名就是函数的内存地址,而函数名()则是运行这个函数. def func(): return print(func) # 返回一个地址 # 2,函数名可以作为变量. def ...

  3. python 嵌套作用域 闭包函数

    #闭包函数 def multiplier(factor): def multiplyByFactory(number): return number*factor return multiplyByF ...

  4. 【python】函数闭包

    列表时可以改

  5. Python中函数的嵌套及闭包

    函数的嵌套 调用:在函数中调用函数 定义:在函数中定义函数 地址:函数名有内存地址,内存地址可赋值 示例 a = 1 def outer(): a = 1 def inner(): a = 2 def ...

  6. ~~函数基础(三):嵌套函数&匿名函数~~

    进击のpython 嵌套函数&匿名函数 讲完作用域之后 对变量的作用范围有大致的了解了吗? 讲个稍微小进阶的东西吧 能够帮助你更加的理解全局和局部变量 嵌套函数 玩过俄罗斯套娃不? 没玩过听过 ...

  7. 一文搞懂Python函数(匿名函数、嵌套函数、闭包、装饰器)!

    Python函数定义.匿名函数.嵌套函数.闭包.装饰器 目录 Python函数定义.匿名函数.嵌套函数.闭包.装饰器 函数核心理解 1. 函数定义 2. 嵌套函数 2.1 作用 2.2 函数变量作用域 ...

  8. python基础—函数嵌套与闭包

    python基础-函数嵌套与闭包 1.名称空间与作用域 1 名称空间分为: 1 内置名称空间   内置在解释器中的名称 2 全局名称空间   顶头写的名称 3 局部名称空间 2 找一个名称的查找顺序: ...

  9. Python之函数对象、函数嵌套、名称空间与作用域、闭包函数、装饰器

    目录 一 函数对象 二 函数的嵌套 三 名称空间和作用域 四 闭合函数 五 装饰器 一.函数对象 1.函数是第一类对象 #第一类对象的特性:# 可以被引用 # 可以当做参数传递 # 返回值是函数 # ...

随机推荐

  1. Flooded!

    Flooded! Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 5955   Accepted: 1800   Specia ...

  2. HTML 学习笔记 JavaScript (prototype)

    原博地址:http://www.cnblogs.com/dolphinX/p/3286177.html 原博客的作者是一个非常牛逼的前端大神,我作为一个初学者,在此借助大神的博客进行自己的学习.在这里 ...

  3. WINDOW的cmd的命令【转载】

    转载地址:https://zhidao.baidu.com/question/583956458.htmlwinver---------检查windows版本 wmimgmt.msc----打开win ...

  4. Openjudge 1.13-28:出现次数超过一半的数

    总时间限制:  1000ms 内存限制:  65536kB 描述 给出一个含有n(0 < n <= 1000)个整数的数组,请找出其中出现次数超过一半的数. 数组中的数大于-50且小于50 ...

  5. C#.NET 大型企业信息化系统集成快速开发平台 4.1 版本 - 如何才能成为全国知名软件组件

    往往我们看到一个好用的工具.就能知道制作这个工具有多少不容易,使用好这个工具也有多少不容易? 通用快速开发框架同样也是经过多年的完善改进才到了今天的稳定成熟度,知名程度,为什么能成为全国有名的软件组件 ...

  6. npm设置prefix 路径

    Windows下的Nodejs npm路径是appdata,很不爽,想改回来,但是在cmd下执行以下命令也无效 npm config set cache "D:\nodejs\node_ca ...

  7. 在intellj idea下用sbt的坑

    version : SBT 0.13.7 intellij 14 新建SBT项目以后无法编译,总是显示 Can not resolve symble stackoverflow 给出了暂时的解决办法. ...

  8. go 数组(array)、切片(slice)、map、结构体(struct)

    一 数组(array) go语言中的数组是固定长度的.使用前必须指定数组长度. go语言中数组是值类型.如果将数组赋值给另一个数组或者方法中参数使用都是复制一份,方法中使用可以使用指针传递地址. 声明 ...

  9. iOS 如何打开后灯(闪光灯)

    - (void)torchOnOrOff { AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMedia ...

  10. 基于Spring4+Hibernate4的通用数据访问层+业务逻辑层(Dao层+Service层)设计与实现!

    基于泛型的依赖注入.当我们的项目中有很多的Model时,相应的Dao(DaoImpl),Service(ServiceImpl)也会增多. 而我们对这些Model的操作很多都是类似的,下面是我举出的一 ...