Python闭包
1、概述
闭包是在其词法上下文中引用自由变量的函数。
- >>> def foo():
- ... m=3
- ... n=5
- ... def bar():
- ... a=4
- ... return m+n+a
- ... return bar
- ...
- >>> foo()
- <function bar at 0x7f9eb9549848>
- >>> bar=foo()
- >>> bar()
- 12
说明:
bar在foo函数的代码块中定义。bar是foo的内部函数。在bar的局部作用域中可以直接访问foo局部作用域中定义的m、n变量。这种内部函数可以使用外部函数变量的行为就叫闭包。
python中闭包表示内部函数,由一个变量指代,而这个变量对于外层包含的函数而言是本地变量。
2、code object
code object是python代码经过编译后的对象,它用来存储一些与代码有关的信息以及bytecode。
- >>> code_obj = compile('sum([1,2,3])','','single')
- >>>
- >>> exec(code_obj)
- 6
- >>>
- >>> dis.dis(code_obj)
- 1 0 LOAD_NAME 0 (sum)
- 3 LOAD_CONST 0 (1)
- 6 LOAD_CONST 1 (2)
- 9 LOAD_CONST 2 (3)
- 12 BUILD_LIST 3
- 15 CALL_FUNCTION 1
- 18 PRINT_EXPR
- 19 LOAD_CONST 3 (None)
- 22 RETURN_VALUE
- >>>
上述演示如何通过编译产生code object以及使用exec运行该代码,和使用dis方便地查看字节码。
3、cell object
cell对象的引入,是为了实现被多个作用域引用的变量。对每一个这样的变量,都用一个cell对象来保存其值。
拿之前的示例来说,m和n既在foo函数的作用域中被引用,又在bar函数的作用域中被引用,所以m,n引用的值,都会在一个cell对象中。
可以通过内部函数的__closure__或者func_closure特性查看cell对象
- >>> bar.func_closure
- (<cell at 0x7f9eb954fad0: int object at 0x6092f8>, <cell at 0x7f9eb954fb78: int object at 0x6092c8>)
这两个int型的cell分别存储了m和n的值
无论在外部函数中定义,还是在内部函数中调用,引用的指向都是cell对象。
注:内部函数无法修改cell对象中的值,如果尝试修改m的值,编译器会认为m是函数bar的局部变量,同时foo代码块中的m也会被认为是函数foo的局部变量,就会再把m认作闭包变量,两个m分别在各自的作用域下起作用。
4、闭包分析
- >>> foo.func_code.co_code
- 'd\x01\x00\x89\x00\x00d\x02\x00\x89\x01\x00\x87\x00\x00\x87\x01\x00d\x03\x00\x86\x00\x00}\x00\x00|\x00\x00S'
- >>> code_obj=foo.func_code.co_code
- >>> dis.dis(code_obj)
- 0 LOAD_CONST 1 (1)
- 3 STORE_DEREF 0
7 6 LOAD_CONST 2 (2)- 9 STORE_DEREF 1
9 12 LOAD_CLOSURE 0- 15 LOAD_CLOSURE 1
- 18 LOAD_CONST 3 (3)
- 21 MAKE_CLOSURE 0
- 24 STORE_FAST 0 (0)
- 27 LOAD_FAST 0 (0)
- 30 RETURN_VALUE
- 2 0 LOAD_CONST 1 (3)
- 3 STORE_DEREF 0 (m)
- 3 6 LOAD_CONST 2 (5)
- 9 STORE_DEREF 1 (n)
- 4 12 LOAD_CLOSURE 0 (m)
- 15 LOAD_CLOSURE 1 (n)
- 18 BUILD_TUPLE 2
- 21 LOAD_CONST 3 (<code object bar at 018D9848, file "<pyshell#1>", line 4>)
- 24 MAKE_CLOSURE 0
- 27 STORE_FAST 0 (bar)
- 7 30 LOAD_FAST 0 (bar)
- 33 RETURN_VALUE
LOAD_CONST 1 (3) :
将foo.func_code.co_consts [1]
的值"3"压入栈。
STORE_DEREF 0 (m) :
从栈顶Pop出"3"包装成cell对象存入cell与自由变量的存储区的第0槽。
将cell对象的地址信息赋给变量m(闭包变量名记录在func_code.cellvars)。
func_code.cellvars的内容为('m', 'n')
LOAD_CLOSURE 0 (m) :
将变量m的值压入栈,类似如下信息:
<cell at 0x01D572B0: int object at 0x0180D6F8>
LOAD_CLOSURE 1 (n) :
类似变量m的处理,不在累述。
当前栈区状态:
1 | <cell at 0x01D572B0: int object at 0x0180D6F8> |
2 | <cell at 0x01D86510: int object at 0x0180D6E0> |
3 | … |
BUILD_TUPLE 2 :
将栈顶的两项取出,创建元组,并将该元组压入栈。
LOAD_CONST 3 :
从foo.func_code.co_consts [3]
取出,该项为内部函数bar的code object的地址,将其压入栈
<code object bar at 018D9848, file "<pyshell#1>", line 4>
栈区状态:
1 | <code object bar at 018D9848, file "<pyshell#1>", line 4> |
2 | (<cell at 0x01D572B0: int object at 0x0180D6F8>, <cell at 0x01D86510: int object at 0x0180D6E0>) |
3 | … |
MAKE_CLOSURE 0 :
创建一个函数对象,将位于栈顶的code object(bar函数的code)地址信息赋
给该函数对象的func_code特性;
将栈顶第二项(包含cell对象地址的元组)赋给该函数对象的func_closure特性;
最后将该函数对象地址信息压入栈。
STORE_FAST 0 (bar) :
从栈顶取出之前创建的函数对象的地址信息赋给局部变量bar(局部变量名记录在func_code.co_varnames中)
func_code.co_varnames的内容为('bar',)
将变量bar(记录在func_code.cellvars [0]
)绑定栈顶的函数对象地址。
LOAD_FAST 0 (bar) :
将变量bar的值压入栈。
RETURN_VALUE
返回栈顶项,print bar可以看到<function bar at 0x01D899F0>
- 再分析bar函数就简单了
- 5 0 LOAD_CONST 1 (4)
- 3 STORE_FAST 0 (a)
- 6 6 LOAD_DEREF 0 (m)
- 9 LOAD_DEREF 1 (n)
- 12 BINARY_ADD
- 13 LOAD_FAST 0 (a)
- 16 BINARY_ADD
- 17 RETURN_VALUE
重点是LOAD_DEREF,该方法主要是将cell对象中的object内容压入栈。大致过程如下:
根据变量m的值找到包装在cell内的int object的地址信息
m的值:<cell at 0x01D572B0: int object at 0x0180D6F8>
根据地址取出int值,压入栈。
http://www.cnblogs.com/ChrisChen3121/p/3208119.html
Python闭包的更多相关文章
- Python 闭包
什么是闭包? 闭包(closure)是词法闭包(lexical closure)的简称.闭包不是新奇的概念,而是早在高级程序语言开始发展的年代就已产生. 对闭包的理解大致分为两类,将闭包视为函数或者是 ...
- Python闭包与函数对象
1. Python闭包是什么 在python中有函数闭包的概念,这个概念是什么意思呢,查看Wikipedia的说明如下: “ In programming languages, closures (a ...
- Python闭包及装饰器
Python闭包 先看一个例子: def outer(x): def inner(y): return x+y return innder add = outer(8) print add(6) 我们 ...
- Python闭包详解
Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...
- Python闭包及其作用域
Python闭包及其作用域 关于Python作用域的知识在python作用域有相应的笔记,这个笔记是关于Python闭包及其作用域的详细的笔记 如果在一个内部函数里,对一个外部作用域(但不是全局作用域 ...
- Python 闭包小记
闭包就是能够读取其他函数内部变量的函数.例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“.在本质上,闭包是将函数内部和函数外部连接起来 ...
- 理解Python闭包概念
闭包并不只是一个python中的概念,在函数式编程语言中应用较为广泛.理解python中的闭包一方面是能够正确的使用闭包,另一方面可以好好体会和思考闭包的设计思想. 1.概念介绍 首先看一下维基上对闭 ...
- Python闭包举例
Python闭包的条件: 1.函数嵌套.在外部函数内,定义内部函数. 2.参数传递.外部函数的局部变量,作为内部函数参数. 3.返回函数.外部函数的返回值,为内部函数. 举例如下: def line_ ...
- python 闭包和装饰器
python 闭包和装饰器 一.闭包闭包:外部函数FunOut()里面包含一个内部函数FunIn(),并且外部函数返回内部函数的对象FunIn,内部函数存在对外部函数的变量的引用.那么这个内部函数Fu ...
- Python 闭包(Closure)
Python 闭包 (Closure) 这里介绍一下python 的闭包 基本概念 闭包(closure)是函数式编程的重要的语法结构. 函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函 ...
随机推荐
- springboot与thrift集成实现服务端和客户端
我们这里用一个简单的小功能来演示一下如何使用springboot集成thrift 这个功能是,判断hdfs路径存在. 1.先解决依赖 <dependencies> <dependen ...
- python apschedule安装使用与源码分析
我们的项目中用apschedule作为核心定时调度模块.所以对apschedule进行了一些调查和源码级的分析. 1.为什么选择apschedule? 听信了一句话,apschedule之于pytho ...
- 多线程(RunLoop)
1.RunLoop的概念及作用 2.RunLoop的使用 3.RunLoop的相关类 4.RunLoop的工作原理 5.小结 6.思考 什么是RunLoop? 从字面意思上是一直循环跑,事实上就是一个 ...
- 【学习】js学习笔记:对象的一些基础知识
1.创建对象 //构造函数方法 function fun1(){} var obj=new fun1(); //Object方法,O要大写 var obj1=new Object(); //json方 ...
- HTML5之window.applicationCache对象
不知道离线缓存技术的可以参照上一篇文章: HTML5之appcache语法理解/HTML5应用程序缓存/manifest缓存文件官方用法翻译 参考文章 window.applicationCache ...
- [转载]Vector用法(C++ Primer中文版)
转自:http://blog.sciencenet.cn/blog-261330-551086.html vector 是同一种类型的对象的集合,每个对象都有一个对应的整数索引值.和 string 对 ...
- C 实现可变参数
C中可以借助va_list实现可变参数: va_start:使用传入的可变参数的第一个变量初始化va_list va_arg:获取当前可变参数,每次调用时会将指针向后移 va_end:结束 利用这个机 ...
- 如何获取系统Home(Launcher)应用判断用户是否处于home界面
要把我们的应用程序作为home(launcher应用),只需要在AndroidManifest.xml中添加: <category android:name="android.inte ...
- zookeeper启动后的注意事项
在各节点中运行zkServer.sh start后 1.首先看进程是否存在 QuorumPeerMain. 2.查看zookeeper的运行状态 zkServer.sh status,会出现一个lea ...
- Be the Winner
Be the Winner Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...