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