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计算牛顿迭代多项式实例分析
May 07 Python
对Python的Django框架中的项目进行单元测试的方法
Apr 11 Python
Python的mysql数据库的更新如何实现
Jul 31 Python
django实现用户登陆功能详解
Dec 11 Python
Python实现读取txt文件并转换为excel的方法示例
May 17 Python
python实现动态数组的示例代码
Jul 15 Python
Django使用Jinja2模板引擎的示例代码
Aug 09 Python
python是否适合网页编程详解
Oct 04 Python
python实现飞机大战小游戏
Nov 08 Python
python3-flask-3将信息写入日志的实操方法
Nov 12 Python
Python3如何使用tabulate打印数据
Sep 25 Python
利用Python判断整数是否是回文数的3种方法总结
Jul 07 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操作符与控制结构代码
2011/12/30 PHP
深入理解PHP中的global
2014/08/19 PHP
用php来限制每个ip每天浏览页面数量的实现思路
2015/02/24 PHP
PHP实现的蚂蚁爬杆路径算法代码
2015/12/03 PHP
详解WordPress开发中get_header()获取头部函数的用法
2016/01/08 PHP
深入剖析PHP中printf()函数格式化使用
2016/05/23 PHP
PHP判断当前使用的是什么浏览器(推荐)
2019/10/27 PHP
利用404错误页面实现UrlRewrite的实现代码
2008/08/20 Javascript
利用webqq协议使用python登录qq发消息源码参考
2013/04/08 Javascript
Ext GridPanel加载完数据后进行操作示例代码
2014/06/17 Javascript
浅析四种常见的Javascript声明循环变量的书写方式
2015/10/14 Javascript
layui中layer前端组件实现图片显示功能的方法分析
2017/10/13 Javascript
ES6基础之解构赋值(destructuring assignment)
2019/02/21 Javascript
el-table表头根据内容自适应完美解决表头错位和固定列错位
2021/01/07 Javascript
Python Deque 模块使用详解
2014/07/04 Python
Python import自定义模块方法
2015/02/12 Python
Django自定义过滤器定义与用法示例
2018/03/22 Python
使用Scrapy爬取动态数据
2018/10/21 Python
对python条件表达式的四种实现方法小结
2019/01/30 Python
python实现中文文本分句的例子
2019/07/15 Python
python同步windows和linux文件
2019/08/29 Python
Python函数参数类型及排序原理总结
2019/12/19 Python
python 使用递归实现打印一个数字的每一位示例
2020/02/27 Python
python中逻辑与或(and、or)和按位与或异或(&amp;、|、^)区别
2020/08/05 Python
Python 中 sorted 如何自定义比较逻辑
2021/02/02 Python
requests在python中发送请求的实例讲解
2021/02/17 Python
使用canvas压缩图片大小的方法示例
2019/08/02 HTML / CSS
博士学位自我鉴定范文
2013/12/26 职场文书
销售经理岗位职责
2014/03/16 职场文书
师德师风个人反思
2014/04/28 职场文书
司法建议书范文
2014/05/13 职场文书
2015年学校精神文明工作总结
2015/05/27 职场文书
永远是春天观后感
2015/06/12 职场文书
小学运动会通讯稿
2015/07/18 职场文书
MySQL 四种连接和多表查询详解
2021/07/16 MySQL
SQL Server中的逻辑函数介绍
2022/05/25 SQL Server