Python 闭包函数详解:原理、判断与封装特性

认真发呆
认真发呆
发布时间:2026-10-16 | 6人浏览过
原创

本文深入解析 Python 闭包函数的形成条件、判断方法及核心特性。通过 __closure__ 和 cell_contents 属性识别闭包,探讨内函数如何延长外函数局部变量生命周期,并演示如何利用闭包封装数据以保护状态不被全局污染,适用于需要状态保持的场景。

Python 函数进阶之闭包函数

什么是闭包函数

当内部函数引用了外部函数的局部变量,且外部函数将该内部函数作为返回值返回时,这一过程即构成闭包,其中被返回的内部函数称为闭包函数。

# 外函数 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__

可通过 __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 执行完毕后,其局部变量 val 本应被释放,但由于闭包函数 inner 引用了 val,val 的生命周期被延长。因此,val 的值 10 会一直保留在内存中,供 inner 函数在后续调用时继续使用。因为 inner 使用了 outer 的变量 val,且 outer 返回了 inner,所以 inner 是闭包函数。当 inner 调用 outer 的变量 val 时,val 的生命周期得以延长,不会随 outer 调用结束而释放,而是存储在内存中,供 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 相关产品和应用

全部下载

专题列表