浅析Python闭包
1、什么是闭包
在介绍闭包概念前,我们先来看一段简短的代码
def sum_calc(*args):
def wrapper():
sum = 0
for n in args:
sum += n;
return sum
return wrapper
很显然,这段代码定义了一个名为sum_calc的函数,但和定义的普通函数不同的是这个函数体的内部又定义了一个名为wrapper的函数,并且sum_calc函数的返回值是内部定义wrapper函数。
现在我们开始来调用sum_calc函数,看看会出现哪些有趣的事情
>>> f = sum_calc(1, 2, 3, 4, 5)
>>> f
<function sum_calc.<locals>.wrapper at 0x0000025693AC2D30>
>>> f()
15
从运行结果来看,当我们调用sum_calc时,返回的并不是求和结果,而是内部定义的求和函数。
继续调用sum_calc返回函数时,才真正计算出求和的结果。
当我们继续调用一次sum_calc,传入相同参数
>>> f1 = sum_calc(1, 2, 3, 4, 5)
>>> f2 = sum_calc(1, 2, 3, 4, 5)
>>> f1 == f2
False
>>> f1()
15
>>> f2()
15
每次调用sum_calc,即使传入相同的参数,两次返回的对象不同,且f1()和f2()的结果互不影响。
在这个例子中,我们在sum_calc函数中定义的wrapper函数,wrapper函数可以引用外部函数sum_calc的参数和局部变量,当sum_calc返回函数wrapper时,相关参数保存在返回的函数中,可以被返回的函数继续使用,我们把这种情况称为“闭包”。
参考维基百科,对“闭包”进行更严谨的解释:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
2、闭包陷阱
def my_fun():
fs = []
for i in range(0, 3):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都放在列表中返回。
我们可能认为,f1()、f2()、f3()结果是0、1、4,但实际结果是4
>>> f1()
4
>>> f2()
4
>>> f3()
4
这个例子可以说是典型的错误使用闭包的案例,my_fun返回的并不是一个闭包函数,而是一个包含三个闭包函数的一个列表。
在返回闭包列表fs之前for循环的变量的值已经发生改变了,循环内闭包函数仅声明了自己计算方式,并不会立即使用当前i的值进行计算。只有在真正调用闭包函数时,才会真正的执行闭包函数内的计算,而此时存放的i的值已经是2,所以f1()、f2()、f3()的结果是4而不是我在之前期待的0、1、4。
经过上面的分析,我们得出下面一个重要的经验:返回闭包中不要引用任何循环变量,或者后续会发生变化的变量。
正确写法
def my_fun(*args):
L = []
for i in range(3):
def wrapper(_i = i):
return _i * _i
L.append(wrapper)
return L
3、闭包实现机制
闭包比普通的函数多了一个 __closure__ 属性,该属性记录着自由变量内容。当闭包被调用时,系统就会根据该地址找到对应的自由变量,完成整体的函数调用
还以 sum_calc() 为例,当其被调用时,可以通过 __closure__ 属性获取自由变量(也就是程序中的 args参数)内容,例如:
def sum_calc(*args):
def wrapper():
sum = 0
for n in args:
sum += n;
return sum
return wrapper
输出结果:
>>> f1 = sum_calc(1, 2, 3, 4, 5)
>>> f1.__closure__
(<cell at 0x0000025693A5E4C0: tuple object at 0x0000025693AE5400>,)
>>> f1.__closure__[0].cell_contents
(1, 2, 3, 4, 5)
可以看到,显示的内容是一个tuple类型,这就是f1中自由变量args的值。还可以看到,__closure__ 属性的类型是一个元组,这表明闭包可以支持多个自由变量的形式。
浅析Python闭包的更多相关文章
- 浅析Python装饰器
1.什么是装饰器 在介绍装饰器之前,我们先来思考一个问题:使用Python语言进行程序设计时,如果我们想扩展一个函数的功能,一般会怎么做呢? 比如,有一个名为print_info函数,当前该函数内只做 ...
- 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_ ...
随机推荐
- Linux 系统故障排查,这里看过来
来源:https://www.jianshu.com/p/0bbac570fa4c 导读 有时候会遇到一些疑难杂症,并且监控插件并不能一眼立马发现问题的根源.这时候就需要登录服务器进一步深入分析问题的 ...
- python - os.sep用法
python是跨平台的.在Windows上,文件的路径分隔符是'\',在Linux上是'/'.为了让代码在不同的平台上都能运行,那么路径应该写'\'还是'/'呢?使用os.sep的话,就不用考虑这个了 ...
- 领域设计:Entity与VO
本文探讨如下内容: 什么是状态 什么是标识 什么是Entity 什么是VO(ValueObject) 在设计中如何识别Entity和VO 要理解Entity和VO,需要先理解两个概念:「状态」和「标识 ...
- golang 自学系列(四)——debug for vscode
golang 自学系列(四)--(调试)VSCode For Debug 这里如何装 vscode 我就不说了 这里如何在 vscode 正常写代码我也不说了 在能正常用 vscode 写 go 语言 ...
- spring boot 访问外部http请求
以前 访问外部请求都要经过 要用 httpClient 需要专门写一个方法 来发送http请求 这个这里就不说了 网上一搜全都是现成的方法 springboot 实现外部http请求 是通过F ...
- 无所不能的Embedding5 - skip-thought的兄弟们[Trim/CNN-LSTM/quick-thought]
这一章我们来聊聊skip-thought的三兄弟,它们在解决skip-thought遗留问题上做出了不同的尝试[Ref1-4], 以下paper可能没有给出最优的解决方案(对不同的NLP任务其实没有最 ...
- PyQt学习随笔:重写setData方法截获Model/View中视图数据项编辑的注意事项
根据<PyQt学习随笔:Model/View中视图数据项编辑变动实时获取变动数据的方法>可以重写从PyQt的Model类继承的setData方法来实时截获View中对数据的更改,但需要注意 ...
- B站自动爬取器并制作词云
效果 词云展示 弹幕展示 爬取弹幕过程 基本步骤 1.寻找视频url 2.构造请求头 3.寻找弹幕地址 4.根据弹幕地址运用正则或xpath爬取 寻找B站视频的url 制作请求头 headers = ...
- pycharm 本地代码同步到github上
第一步, pycharm中setting-> Version Control -> Github -> addacount 如果账号密码登录不成功,就用token登录,参考下面这个博 ...
- LeetCode初级算法之数组:66 加一
加一 题目地址:https://leetcode-cn.com/problems/plus-one/ 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一.最高位数字存放在数组的首位, 数 ...