python 闭包函数详细介绍


Posted in Python onApril 19, 2022

闭包函数

什么是闭包函数

如果内函数使用了外函数的局部变量,并且外函数把内函数返回出来的过程叫做闭包,里面的内函数是闭包函数。

# 外函数 outer
def outer():
# 外函数变量 num
var = '外函数局部变量'

# 内函数 inner
def inner():
# 内函数使用了外函数的变量 num
print('内函数使用了:' + var)

# 外函数将使用了外函数的局部变量的内函数返回
return inner

# 返回出的结果就是内函数 inner,现在inner就是一个闭包函数
func = outer()

# 执行返回出的 inner 函数
func() # 内函数使用了:外函数局部变量

下面是一个复杂的版本。
inner函数返回了函数x 和 y,x 和 y是外函数的内函数,虽然覆盖了原有的外函数的局部变量,但是这两个函数本质上还是外函数的布局变量,所以外函数返回了inner,inner就是一个闭包函数。
inner返回了外函数的x和y函数,x和y函数都是用了外函数的内函数num3,外函数返回inner,inner返回了x和y,所以变相的就是外函数返回了x和y,所以x和y也是闭包函数。

# 外函数
def outer():
# 外函数的局部变量
x = 1
y = 2
num3 = 3

# 内函数 x 重名变量 x
def x():
# 调用修改了 变量 num3
nonlocal num3
num3 *= 10
print(num3)

# 内函数 y 重名变量y
def y():
# 调用修改了 变量num3
nonlocal num3
num3 += 10
print(num3)

# 内函数inner
def inner():
# 返回了同级内函数 x y
return x, y

# 外函数最终返回了 inner函数
return inner

判断是否是闭包函数

方法

作用

\__closure__

获取闭包函数使用的局部变量

cell_contents

获取单元格对象当中的闭包函数

\__closure__

可以使用这个方法判断一个函数是否是一个闭包函数,因为闭包函数必须要使用外函数的局部变量,如果返回None就说明这个函数不是闭包函数,如果返回的是一个元组,说明这是一个闭包函数,元组中有cell单元格对象,一个单元格对象表示这个闭包函数使用了几个外函数的局部变量。
拿上述版本测试。

# 外函数
def outer():
# 外函数的局部变量
x = 1
y = 2
num3 = 3

# 内函数 x 重名变量 x
def x():
# 调用修改了 变量 num3
nonlocal num3
num3 *= 10
print(num3)

# 内函数 y 重名变量y
def y():
# 调用修改了 变量num3
nonlocal num3
num3 += 10
print(num3)

# 内函数inner
def inner():
# 返回了同级内函数 x y
return x, y

# 外函数最终返回了 inner函数
return inner


# 执行outer返回的结果是inner
func = outer() # func == inner

# 执行func返回的是 x y 函数
a, b = func()

# 使用__closure__测试这个几个函数是否是闭包函数
print(outer.__closure__)
print(func.__closure__)
print(a.__closure__)
print(b.__closure__)

'''
结果:除了外函数outer之外都返回了cell对象,说明inner x y 都是闭包函数
None
(<cell at 0x0000022F246AECA8: function object at 0x0000022F2466C400>, <cell at 0x0000022F247F3558: function object at 0x0000022F24850730>)
(<cell at 0x0000022F245D8708: int object at 0x00000000542280B0>,)
(<cell at 0x0000022F245D8708: int object at 0x00000000542280B0>,)
'''

cell_contents

虽然用​​__closure__​​获取到了闭包函数使用的元素,但是是以cell单元格对象的形式展示的,我们并不能看出这个使用的 元素到底是什么东西,可以使用​​cell_contents​​查看。

# 外函数
def outer():
# 外函数的局部变量
x = 1
y = 2
num3 = 3

# 内函数 x 重名变量 x
def x():
# 调用修改了 变量 num3
nonlocal num3
num3 *= 10
print(num3)

# 内函数 y 重名变量y
def y():
# 调用修改了 变量num3
nonlocal num3
num3 += 10
print(num3)

# 内函数inner
def inner():
# 返回了同级内函数 x y
return x, y

# 外函数最终返回了 inner函数
return inner


# 执行outer返回的结果是inner
func = outer() # func == inner


# 使用__closure__返回了闭包函数使用的局部变量
tup = func.__closure__

# 使用 cell_contents 查看这些局部变量都是些什么
res = tup[0].cell_contents
print(res)
res = tup[1].cell_contents
print(res)

'''
结果:可以看到inner 使用的局部变量使用外函数的内函数 x 和 y
None
<function outer.<locals>.x at 0x0000018D5A66C400>
<function outer.<locals>.y at 0x0000018D5A850730>
'''

闭包函数的特点

让我们回忆一下,函数中创建的变量是一个什么变量?是一个局部变量。
局部变量的生命周期是多久?是等局部作用结束之后就会被释放掉。

如果内函数使用了外函数的局部变量,那么这个变量就与闭包函数发生了绑定关系,就延长该变量的生命周期。实际上就是内存给它存储了这个值,暂时不释放。

下面的例子中,我们调用了函数outer并赋予了参数val的值为10,但是outer执行完之后,outer的val并没有被释放,而是被闭包函数inner延长了生命周期,所以val可以一直在inner中按照调用outer函数的时候赋予的值10进行运算。
因为内函数inner使用了外函数outer的变量val,且outer返回了inner,所以inner是一个闭包函数。因为inner是一个闭包函数,当它调用outer的变量val时就会延长val的生命周期,val就不会随着outer的调用结束而被释放
而是存储在了内存当中,当inner再次使用val时,val就会将值赋予inner。

def outer(val):
def inner(num):
return val + num

return inner

func = outer(10)
res = func(10)
print(res) # 20
res = func(20)
print(res) # 30

闭包函数的意义

闭包可以优先使用外函数中的变量,并对闭包中的值起到了封装包保护的作用,使外部无法访问。
我们做一个模拟鼠标点击的事件,可以看得出闭包函数封装保护数据的作用。
现在只是一个普通的函数,它无法对我们使用的变量的数据进行保护,在全局中这个数据可以被随意的修改。

# 不使用闭包,当函数中调用全局变量时,外部也可以控制变量

# 全局变量
num = 0

# 点击事件
def click_num():
# 每执行一次数值 +1
global num
num += 1
print(num)

# 执行点击事件
click_num() # 1
click_num() # 2
click_num() # 3

# 在全局重新定义了num的值,num的值就被彻底的改变了,但是我们的程序的数据本不该如此。
num = 1231231

click_num() # 1231232
click_num() # 1231233
click_num() # 1231234

现在使用闭包函数对数据进行封装保护,就不能在全局中随意的修改我们使用的数据。

# 我们将需要使用的数据放在外函数中,点击事件作为内函数也放在外函数中,然后作为闭包返回。
def clickNum():
# 需要使用的数据
num = 0

# 内函数(真正执行点击事件的函数)
def inner():
# 执行点击事件
nonlocal num
num += 1
print(num)

# 作为闭包返回
return inner

# 返回闭包
click_num = clickNum() # # click_num == inner

# 执行点击事件
click_num() # 1
click_num() # 2
click_num() # 3

# 全局中修改 num 的值
num = 123412341234

# 闭包函数对数据的封装保护起到了作用
click_num() # 4
click_num() # 5
click_num() # 6

到此这篇关于python 函数进阶之闭包函数的文章就介绍到这了!

Python 相关文章推荐
Python标准库与第三方库详解
Jul 22 Python
Python使用scrapy采集数据时为每个请求随机分配user-agent的方法
Apr 08 Python
Python的Django框架可适配的各种数据库介绍
Jul 15 Python
在Python的Django框架中更新数据库数据的方法
Jul 17 Python
python实现八大排序算法(1)
Sep 14 Python
Python3 伪装浏览器的方法示例
Nov 23 Python
python3.6使用pickle序列化class的方法
Oct 22 Python
python 对类的成员函数开启线程的方法
Jan 22 Python
python检测服务器端口代码实例
Aug 31 Python
python读取ini配置的类封装代码实例
Jan 08 Python
Python代码覆盖率统计工具coverage.py用法详解
Nov 25 Python
通用的Django注册功能模块实现方法
Feb 05 Python
Python  lambda匿名函数和三元运算符
Apr 19 #Python
Python使用mitmproxy工具监控手机 下载手机小视频
使用Python通过企业微信应用给企业成员发消息
Python用any()函数检查字符串中的字母以及如何使用all()函数
Apr 14 #Python
python在package下继续嵌套一个package
Apr 14 #Python
Github 使用python对copilot做些简单使用测试
使用Python拟合函数曲线
Apr 14 #Python
You might like
PHP mysql事务问题实例分析
2016/01/18 PHP
Thinkphp框架开发移动端接口(2)
2016/08/18 PHP
php处理抢购类功能的高并发请求
2018/02/08 PHP
细品javascript 寻址,闭包,对象模型和相关问题
2009/04/27 Javascript
javascript 构造函数强制调用经验总结
2012/12/02 Javascript
JavaScript实现维吉尼亚(Vigenere)密码算法实例
2013/11/22 Javascript
纯javascript实现简单下拉刷新功能
2015/03/13 Javascript
深入学习JavaScript中的原型prototype
2015/08/13 Javascript
JavaScript简单判断复选框是否选中及取出值的方法
2015/08/13 Javascript
javascript中return,return true,return false三者的用法及区别
2015/11/17 Javascript
jQuery实现伪分页的方法分享
2016/02/17 Javascript
jQuery实现的网格线绘制方法
2016/06/20 Javascript
js 将图片连接转换成base64格式的简单实例
2016/08/10 Javascript
vue调用高德地图实例代码
2017/04/28 Javascript
Node.JS文件系统解析实例详解
2017/05/15 Javascript
JS中判断某个字符串是否包含另一个字符串的五种方法
2018/05/03 Javascript
nodejs express配置自签名https服务器的方法
2018/05/22 NodeJs
webpack DllPlugin xxx is not defined解决办法
2019/12/13 Javascript
Vue 同步异步存值取值实现案例
2020/08/05 Javascript
Swiper实现导航栏滚动效果
2020/10/16 Javascript
Python 自动安装 Rising 杀毒软件
2009/04/24 Python
Python通过命令开启http.server服务器的方法
2017/11/04 Python
详解python3中用HTMLTestRunner.py报ImportError: No module named 'StringIO'如何解决
2019/08/27 Python
浅谈python的elementtree模块处理中文注意事项
2020/03/06 Python
25个CSS3动画按钮和菜单教程分享
2012/10/03 HTML / CSS
日本乐天官方海外转运服务:Rakuten Global Express
2018/11/30 全球购物
电子商务专业毕业生工作推荐信
2013/11/17 职场文书
公司出纳岗位职责
2013/12/07 职场文书
回门宴新郎答谢词
2014/01/12 职场文书
《狐假虎威》教学反思
2014/02/07 职场文书
创先争优公开承诺书
2014/08/30 职场文书
局领导领导班子四风对照检查材料
2014/09/27 职场文书
入党积极分子群众意见
2015/06/01 职场文书
致接力运动员加油稿
2015/07/21 职场文书
MySQL8.0.24版本Release Note的一些改进点
2021/04/22 MySQL
Windows Server 2019 配置远程控制以及管理方法
2022/04/28 Servers