浅谈python中的闭包函数
闭包函数初探
通常我们定义函数都是这样定义的
def foo():
pass
其实在函数式编程中,函数里面还可以嵌套函数,如下面这样
def foo():
print("hello world in foo")
def bar():
print("hello world in bar")
此时我们调用foo函数,执行结果会是什么样子的呢??
hello world in foo
结果如上所示,只会执行foo函数的第一层函数,bar函数是不会被执行的。为什么呢
实际上来说,不管函数写在哪个部分,那都只是定义了一个函数,只有这个函数被调用,函数内部的语句才会被执行
在上面的例子中,bar函数虽然在foo函数内部定义了,但是并没有被执行,所以bar函数是不会被执行的
这样说来,定义在一个函数内部的函数就没什么作用了吗??其实不是这样的。
来看下面的例子,把bar函数作为一个值返回给foo函数,来看执行过程
def foo():
print("hello world in foo")
def bar():
print("hello world in bar")
return bar
f1=foo()
print(f1)
此时,由于bar函数作为一个返回值被返回给了foo,所以foo函数执行结果是有返回值的
此时定义一个变量f1来接收foo函数的执行返回结果,然后打印f1
返回的结果如下
hello world in foo
<function foo.<locals>.bar at 0x0000000002941A60>
可以看到首先打印了foo函数中定义的一个print语句,接着打印的是foo函数中包含的bar函数的内存地址
既然是一个函数的内存地址,当然可以加括号来执行这个函数
def foo():
print("hello world in foo")
def bar():
print("hello world in bar")
return bar
f1=foo()
f1()
此时,这段代码的执行结果为:
hello world in foo
hello world in bar
两个print语句都被打印出来了。
在上面的例子里,首先定义了一个函数foo,接着在foo函数内部又嵌套定义了一个函数bar,然后返回函数bar的函数名,这就是闭包函数的定义方式。
其实,闭包的定义就是一个函数内部又嵌套了一个函数
来看下面的这段代码
def foo():
print("hello world in foo")
name="python"
def bar():
print(name)
print("hello world in bar")
return bar
f1=foo()
f1()
在上面的例子里,在外层函数中定义了一个变量name,然后在内层函数中打印这个变量name
此时执行上面的代码,在打印name这个变量的时候,会先在bar函数内部查找name这个变量,但是bar函数里面是没有name这个变量的,
此时根据python查找变量的LEGB
法则,会到bar函数的外面一层去继续查找name这个变量,此时可以找到name这个变量
所以这里打印的foo函数中定义的name的值
执行上面的代码,打印结果如下
hello world in foo
python
hello world in bar
这里要记住很重要的一点就是:
内层函数引用了外层函数的局部变量
来分析下上面的例子中程序的执行过程:
首先运行foo函数,foo函数的执行结果是返回bar的函数名,此时又把foo函数的执行结果定义给了变量f1,
所以此时f1就等于bar这个函数的内存地址,然后f1加括号运行就表示运行了bar函数。
在执行bar函数的过程中,bar函数访问到了外层foo函数中定义的变量,这就是一个典型的闭包函数
那使用闭包函数有什么好处呢??在上面的例子里,f1的值是bar函数的内存地址,f1加括号运行就是在运行bar函数。
又由于f1是一个全局变量,这意味着可以在整个程序的任意位置都可以运行f1函数,此时再定义一个函数,在这个函数内部调用f1函数,
def foo():
print("hello world in foo")
name = "python"
def bar():
print(name)
print("hello world in bar")
return bar
f1 = foo()
def func():
name = "aaaaa"
f1()
func()
来分析一下程序的执行过程:
1.运行func函数,程序会先在内存中申请一块空间以保存name变量的值,然后运行f1函数,f1是在全局中定义的变量,所以一定可以找到f1函数的内存地址
2.f1加括号运行,就是在执行一个闭包函数,这个闭包函数内部引用了name这个变量
3.name这个变量在bar函数的外部已经定义了,所以在func函数内部调用f1函数,也就是bar函数时,其引用的变量依然是foo函数内部定义的name变量,而不是func函数内部定义的name变量,
4.因为f1函数的内部已经包含了name这个函数的值,所以就算在func函数内部也定义了name这个变量,程序执行的结果打印的依然是foo函数内部定义的name的值
程序执行结果
hello world in foo
python
hello world in bar
怎样验证一个函数是闭包函数
首先,闭包函数都有一个特有的属性:closure
在上面的例子里,打印f1的__closure__
属性
def foo():
name = "python"
def bar():
print(name)
print("hello world in bar")
return bar
f1 = foo()
print(f1.__closure__)
打印结果如下:
(<cell at 0x0000000001DF5708: str object at 0x0000000001E79688>,)
可以看到__closure__属性的打印结果是一个元组形式的,其值就是f1函数的外层函数作用域
此时可以调用__closure__返回的元组的元素的cell_contents方法
打印出name变量的值
def foo():
name = "python"
def bar():
print(name)
print("hello world in bar")
return bar
f1 = foo()
print(f1.__closure__[0].cell_contents)
打印结果如下:
python
可以看到程序已经打印出name变量的值了
即然__closure__的返回结果是一个元组,那么这个元组中一定是可以包含多个值的,看下面的例子
在foo函数内部定义多个变量,然后在bar函数内部打印几个变量的值,
然后运行这个闭包函数,打印闭包函数的__closure__方法
def foo():
print("hello world in foo")
name1 = "python1"
name2 = "python2"
name3 = "python3"
name4 = "python4"
def bar():
print(name1)
print(name2)
print(name3)
print(name4)
print("hello world in bar")
return bar
f1 = foo()
print(f1.__closure__)
程序执行结果
(<cell at 0x0000000002145708: str object at 0x00000000021C9260>,
<cell at 0x0000000002145A08: str object at 0x00000000021C93B0>,
<cell at 0x0000000002145768: str object at 0x000000000295BE30>,
<cell at 0x0000000002145C18: str object at 0x0000000002963880>)
由于在foo函数内部定义了4个变量,而且在bar函数内部引用了这4个变量,所以打印这个闭包函数的__closure__方法,返回的元组中就有4个元素
现在可以分别打印返回的元组中的这4个字符串对象的值了
def foo():
name1 = "python1"
name2 = "python2"
name3 = "python3"
name4 = "python4"
def bar():
print(name1)
print(name2)
print(name3)
print(name4)
print("hello world in bar")
return bar
f1 = foo()
print(f1.__closure__[0].cell_contents)
print(f1.__closure__[1].cell_contents)
print(f1.__closure__[2].cell_contents)
print(f1.__closure__[3].cell_contents)
程序执行结果
python1
python2
python3
python4
那么现在还剩下最后一个问题了,那就是闭包函数的内层函数一定要返回吗??
来看下面一个例子
def foo():
name = "python1"
def bar():
print(name)
print(bar.__closure__)
foo()
定义了一个嵌套函数,然后这个嵌套函数的内层函数没有被返回,而是直接打印内层函数的__closure__方法,然后直接调用外层函数。
程序执行结果
(<cell at 0x0000000002155708: str object at 0x00000000021D9688>,)
依然打印出了内层函数的引用的变量对象
这说明闭包函数的内层函数还一定要返回
闭包函数的内层函数可以调用全局变量吗??
把外层函数内部定义的变量改为全局变量,然后在内层函数中引用这个变量
name = "python1"
def foo():
def bar():
print(name)
print(bar.__closure__)
f=foo()
print(f)
程序执行结果
None
None
可以看到,程序的执行结果是两个None,嵌套函数的内层函数的__closure__函数的值为None
这说明foo函数的内层嵌套函数bar调用的全局变量没有成功,所以上面的例子不是一个闭包函数
关于闭包函数的一些总结:
闭包的定义为:
在函数内部定义的函数,称为内部函数
内部函数调用了外部函数的局部变量
即使内部函数返回了,还是可以使用局部变量
通常闭包函数的内层函数都要被返回给外部函数
闭包函数的外部函数可以在任何地方被调用,而不再受函数定义时层级的限制
闭包函数的作用
1.闭包函数自带函数作用域
正常意义上的函数,在函数执行过程中查找变量的顺序是一层一层向外找,符合LEGB(Local->Enclose->Global->Built in)法则的,
但是对闭包函数来说,查找变量只会找内部函数外面的那一层,因为闭包函数本身就自带一层作用域,这样才符合"闭包"两个字的意思
2.延迟计算(也叫惰性计算)
看下面的例子
def func():
name="python"
def bar():
print(name)
return bar
f=func()
print(f.__closure)
在上面的例子里,执行foo()函数的返回结果是一个包含自带的某种状态的函数,实际上这个函数并没有执行,
以后想执行这个自带状态的函数时,把func()返回结果所赋值的那个变量加括号就可以执行了,
3.要想让一个函数始终保持一种状态,就可以使用闭包
例子:
name="python"
def func():
print("I like %s" % name)
func()
上面的代码执行结果会打印一行:"I like python"
但是我们知道,在不同的地方调用func函数,打印的结果很大可能是不一样的
那么如果我想不管在什么地方调用func函数,打印的结果都是"I like python"时,
就可以使用闭包了。
def func1():
name="python"
def func():
print("I like %s" % name)
return func
func=func1()
func()
如上图所示,在func函数外面再包含一层函数func1,执行func1函数,再把func1函数的返回结果赋值给func这个变量
此时func就是一个闭包函数了,把func函数加括号就可以执行了
而且我们一定知道,此时func函数的执行结果一定会打印"I like python"这句话,而且不管func函数在程序的哪个位置被调用,执行结果都是一样的
浅谈python中的闭包函数的更多相关文章
- 浅谈JavaScript中的闭包
浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...
- 浅谈JS中的闭包
浅谈JS中的闭包 在介绍闭包之前,我先介绍点JS的基础知识,下面的基础知识会充分的帮助你理解闭包.那么接下来先看下变量的作用域. 变量的作用域 变量共有两种,一种为全局变量,一种为局部变量.那么全局变 ...
- 浅谈python中得import xxx,from xxx import xxx, from xxx import *
在python中import跟from import都是用来导入的,但是导入的机制不同 1.import xxx:导入模块,或者文件夹,对于调用模块或者文件夹中子模块的变量或者函数,需要使用" ...
- 浅谈python中文件和文件夹的相关操作
文件操作 文件的打开与关闭 打开文件 使用open(文件名,访问方式)函数,可以打开一个已存在的文件,或者创建一个新的文件. 示例如下: f = open('test.txt') # 访问方式可以省略 ...
- 浅谈python中的“ ==” 与“ is”
在python中,== 与 is 之间既有区别,又有联系,本文将通过实际代码的演示,力争能够帮助读到这篇文章的朋友以最短的时间理清二者的关系,并深刻理解它们在内存中的实现机制.扯淡的话不多说,下面马上 ...
- 浅谈python 中正则的一些函数
主要的函数有 : match() search() findall() group() groups() split() match (): 含义 开头匹配,匹配成功返回一个对象失败则 ...
- 浅谈JS中的高级函数
在JavaScript中,函数的功能十分强大.它们是第一类对象,也可以作为另一个对象的方法,还可以作为参数传入另一个函数,不仅如此,还能被一个函数返回!可以说,在JS中,函数无处不在,无所不能,堪比孙 ...
- 浅谈Python中函数式编程、面向对象编程以及古怪的PythonIC
1.函数式编程作为结构化编程的一种,正在受到越来越多的重视.那么什么事函数式编程呢? 在维基百科中给出了详细的定义,函数式编程又称泛函数编程,是一种编程规范,它将函数运算视为数学上的函数计算.简单的来 ...
- 浅谈python中字典append 到list 后值的改变问题
看一个例子 ? 1 2 3 4 d={'test':1} d_test=d d_test['test']=2 print d 如果你在命令行实践的话,会发现你改动的是d_test ,但是d 也跟着改变 ...
随机推荐
- AtCoder Regular Contest 078
我好菜啊,ARC注定出不了F系列.要是出了说不定就橙了. C - Splitting Pile 题意:把序列分成左右两部分,使得两边和之差最小. #include<cstdio> #inc ...
- 常见 Java 异常解释(恶搞版)
常见 Java 异常解释:(译者注:非技术角度分析.阅读有风险,理解需谨慎o(╯□╰)o) java.lang ArithmeticException 你正在试图使用电脑解决一个自己解决不了的数学问题 ...
- UVAlive 3708 Graveyard(最优化问题)
题目描述: 在周长10000的圆上,初始等距的放置着n个雕塑,现在新加入m个雕塑,要使得这n+m个雕塑仍然等距,问原来n个雕塑要移动的距离总和的最小值. 原题地址: http://acm.hust.e ...
- [bzoj2333] [SCOI2011]棘手的操作 (可并堆)
//以后为了凑字数还是把题面搬上来吧2333 发布时间果然各种应景... Time Limit: 10 Sec Memory Limit: 128 MB Description 有N个节点,标号从1 ...
- SPI、I2C、UART(转)
UART与USART(转) UART需要固定的波特率,就是说两位数据的间隔要相等. UART总线是异步串口,一般由波特率产生器(产生的波特率等于传输波特率的16倍).UART接收器.UART发送器组成 ...
- Linux系统常用命令权威指南
<一>线上查询及帮助命令(2)1.man man [选项] [命令] 查看命令帮助,命令的词典,更复杂的还有info,但不常用. #man cd-a 显示所有的手册页,不只是显示第一个-f ...
- 从XMLHttpRequest请求响应里getResponseHeader(header)报错:Refused to get unsafe header "**" 问题解决
问题产生原因: 原因1:W3C的 xhr 标准中做了限制,规定客户端无法获取 response 中的 Set-Cookie.Set-Cookie2这2个字段,无论是同域还是跨域请求: 原因2:W3C ...
- fireasy 使用篇 - 简介
一.Fireasy 简介 Fireasy是一套基于.Net Framework应用开发组件,其主旨思想为“让开发变为更简单”,其义为,使用尽可能少的组件,实现你所需的功能.Fireasy几乎覆盖了开发 ...
- mysql之repair table 修复表札记
REPAIR [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name[,tbl_name] ... [QUICK] [EXTENDED] [USE_FRM] REP ...
- DEDE中如何过滤掉Html标签,并且截取字符串长度
在dede标签中只要使用2个函数就可以. [field:body function="cn_substr(Html2text(@me),80)"/] Html2text()函数是去 ...