文章目录
- @[toc]
- 函数引用
- 示例
- 什么是闭包
- 闭包示例
- 闭包的本质
- 闭包带来的问题
- 使用闭包修改外部函数中的变量
- 闭包与对象的类比
文章目录
- @[toc]
- 函数引用
- 示例
- 什么是闭包
- 闭包示例
- 闭包的本质
- 闭包带来的问题
- 使用闭包修改外部函数中的变量
- 闭包与对象的类比
函数引用
- 在学习闭包之前,先来了解什么是函数引用
示例
def test():print('这是 test 函数的内部代码...')func_obj = testfunc_obj()
这是 test 函数的内部代码...
-
上述代码定义了一个
test()
函数,使用一个变量func_obj
保存了test()
函数的引用(注意test
不要加()
,否则func_obj
保存的是test()
函数的返回值) -
此时
func_obj
保存了test()
函数的引用,即func_obj
指向了test()
函数的代码块在内存中的地址,此时对func_obj
进行调用,就是对func_obj
所指向的代码块进行调用 -
可以看到,调用
func_obj()
后打印了“这是 test 函数的内部代码…” -
id()
函数可以返回对象的唯一标识符,即对象的内存地址,使用id()
函数可以查看此时test()
函数和变量func_obj
的内存地址
def test():print('这是 test 函数的内部代码...')func_obj = testfunc_obj()print(id(test))
print(id(func_obj))
这是 test 函数的内部代码...
1420657021424
1420657021424
- 可以看到此时
test()
函数和变量func_obj
指向同一个内存地址
什么是闭包
- 在一些程序设计语言(例如
Python
)中,在函数中可以嵌套定义另一个函数,如果内部的函数引用了外部函数的变量,并且外部函数的返回值是内部函数的引用,此时被返回的内部函数引用称为闭包 - 换句话说就是,当内部函数的引用被作为返回值返回时,携带了外部函数的变量的信息,就形成了一个闭包
闭包示例
def test(num):def wrapper():print(num)return wrapperfunc_obj = test(0)func_obj()
0
- 上述代码在
test()
函数的内部定义了wrapper()
函数,wrapper()
函数引用了外部函数test()
的变量num
,test()
将wrapper()
函数的引用作为返回值返回 - 由于
wrapper()
函数引用了test()
的变量num
,调用test(0)
时,test()
函数将wrapper()
函数的引用作为返回值返回的同时携带了变量num
的信息 - 然后调用
func_obj()
,打印了 0 0 0,此时运行了print(num)
即print(0)
闭包的本质
- 函数在调用结束后,其参数和局部变量会被内存回收机制回收,无法再被访问
- 闭包携带了包含它的函数的局部变量等信息,实际上是使包含它的函数的作用域在调用后不被内存回收机制所回收而保存在内存中,使闭包能够访问到这些局部变量等信息
闭包带来的问题
- 由于闭包会携带包含它的函数的作用域,因此会比普通函数占用更多的内存
使用闭包修改外部函数中的变量
def counter(cnt):def add():# nonlocal 关键字用于在内层函数中访问并修改外层函数作用域中的变量nonlocal cntcnt += 1return cntreturn add# 创建一个闭包
func_obj = counter(1)print(func_obj())
print(func_obj())
2
3
- 可以看到,变量
cnt
保存在内存中,闭包可以对cnt
进行修改
闭包与对象的类比
- 闭包与对象的功能很相似
- 对象是属性 + + +方法,而闭包可以理解为数据 + + +功能,数据来源于外部函数的局部变量和参数,闭包用于实现功能
- 对象适合实现较复杂的功能,而闭包则更轻量