python中的闭包函数


Posted in Python onFebruary 09, 2018

闭包函数初探

通常我们定义函数都是这样定义的

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 相关文章推荐
python解析发往本机的数据包示例 (解析数据包)
Jan 16 Python
Python正则表达式实现截取成对括号的方法
Jan 06 Python
Python实现的简单模板引擎功能示例
Sep 02 Python
Python用imghdr模块识别图片格式实例解析
Jan 11 Python
Python Web编程之WSGI协议简介
Jul 18 Python
利用pandas进行大文件计数处理的方法
Jul 25 Python
Django实现一对多表模型的跨表查询方法
Dec 18 Python
pandas 使用均值填充缺失值列的小技巧分享
Jul 04 Python
python3 下载网络图片代码实例
Aug 27 Python
python用requests实现http请求代码实例
Oct 31 Python
Django模型层实现多表关系创建和多表操作
Jul 21 Python
Python+Pillow+Pytesseract实现验证码识别
May 11 Python
基于Python socket的端口扫描程序实例代码
Feb 09 #Python
利用python 更新ssh 远程代码 操作远程服务器的实现代码
Feb 08 #Python
Tensorflow 利用tf.contrib.learn建立输入函数的方法
Feb 08 #Python
django数据库migrate失败的解决方法解析
Feb 08 #Python
python使用锁访问共享变量实例解析
Feb 08 #Python
Python异常对代码运行性能的影响实例解析
Feb 08 #Python
Python科学计算包numpy用法实例详解
Feb 08 #Python
You might like
php 操作excel文件的方法小结
2009/12/31 PHP
PHP限制页面只能在微信自带浏览器访问的代码
2014/01/15 PHP
[原创]PHP实现SQL语句格式化功能的方法
2017/07/28 PHP
PHP使用pdo实现事务处理操作示例
2018/09/05 PHP
ExtJs 表单提交登陆实现代码
2010/08/19 Javascript
FusionCharts图表显示双Y轴双(多)曲线
2012/11/22 Javascript
js浮点数保留两位小数点示例代码(四舍五入)
2013/12/26 Javascript
window.open()实现post传递参数
2015/03/12 Javascript
基于JavaScript实现一定时间后去执行一个函数
2015/12/14 Javascript
深入理解JS中的substr和substring
2016/04/26 Javascript
jquery 标签 隔若干行加空白或者加虚线的方法
2016/12/07 Javascript
jQuery实现弹幕效果
2017/02/17 Javascript
ES6 更易于继承的类语法的使用
2019/02/11 Javascript
Vue 组件注册实例详解
2019/02/23 Javascript
浅谈JavaScript面向对象--继承
2019/03/20 Javascript
vue中typescript装饰器的使用方法超实用教程
2019/06/17 Javascript
用map函数来完成Python并行任务的简单示例
2015/04/02 Python
python如何实现excel数据添加到mongodb
2015/07/30 Python
以视频爬取实例讲解Python爬虫神器Beautiful Soup用法
2016/01/20 Python
python类:class创建、数据方法属性及访问控制详解
2016/07/25 Python
python遍历序列enumerate函数浅析
2017/10/17 Python
Python数据分析库pandas基本操作方法
2018/04/08 Python
利用Django模版生成树状结构实例代码
2019/05/19 Python
django ModelForm修改显示缩略图 imagefield类型的实例
2019/07/28 Python
详解python 破解网站反爬虫的两种简单方法
2020/02/09 Python
Python逐行读取文件内容的方法总结
2020/02/14 Python
纯CSS3实现的阴影效果
2014/12/24 HTML / CSS
法国美发器材和产品购物网站:Beauty Coiffure
2016/12/05 全球购物
linux面试题参考答案(4)
2013/01/28 面试题
寄语十八大感言
2014/02/07 职场文书
四风问题民主生活会对照检查材料思想汇报
2014/09/27 职场文书
2014年监理工作总结范文
2014/11/17 职场文书
2015年暑期社会实践方案
2015/07/14 职场文书
大学运动会通讯稿
2015/07/18 职场文书
创业计划书之酒厂
2019/10/14 职场文书
MySQL中InnoDB存储引擎的锁的基本使用教程
2021/05/26 MySQL