当前位置:首页>python>Python快速入门学习笔记十九:闭包

Python快速入门学习笔记十九:闭包

  • 2026-10-11 07:55:35
Python快速入门学习笔记十九:闭包
❝

Python入门第十九课,主要是学习了闭包,闭包初学者较难理解,闭包实质是函数嵌套并引用外部变量,返回内部函数,形成保留外部状态的作用域结构。

  • 前置知识
  • 什么是闭包
  • 闭包的基本形式
  • 闭包是如何保存外层变量的?
  • 注意点
  • 闭包的优点
  • 闭包的缺点

前置知识

在 Python 中学习闭包前,需要先了解以下两点知识:

1、局部作用域的生命周期。

每次调用函数时,都会创建一个新的局部作用域。

函数执行完毕后,该作用域就会被销毁,局部变量随之释放。

测试代码如下:

deftest():    num = 100    num += 10    print(num)test() # 110test() # 110test() # 110# 每次调用 test() 函数,num 都会从新生成,不会保存上一次的值

2、内层函数访问外层变量。

内层函数可以访问到外层函数作用域中的变量。

访问外层变量不用nonlocal,修改外层变量时要使用nonlocal。

defouter():    num = 10definner():        print(num)    inner()outer()

如下的写法,用于验证:理论上num变量应该在outer函数调用完销毁,但实际并没有。

defouter():    num = 10definner():        print(num)return inner # 注意:这里直接返回函数,而不是函数的执行结果result = outer() # outer函数执行print(type(result), result) # <class 'function'> <function outer.<locals>.inner at 0x00000147D3267880>result() # 正常返回num的值:10

什么是闭包

闭包 = 内层函数 + 被内层函数引用的外层变量。

产生闭包的三个条件:

❶ 必须有函数嵌套。

❷ 内层函数使用了外层函数的变量。

❸ 外层函数返回内层函数。

闭包的基本形式

在上述代码的基础上,我们做出这样的调整:在inner函数中不仅要打印num,还要修改num(修改时记得机上nonlocal num)。

最终发现:num的值居然还可以一直增加,所以截止目前,证明了一件事:本应该随着outer函数调用结束而释放或销毁的变量num,并没被释放或销毁,并且inner函数依然可以对num进行读取和修改。

现在我们观察此时的代码形式,完全符合上述的闭包产生条件,所以此时就出现了闭包。

defouter():    num = 10definner():nonlocal num        num += 1        print(num)return inner # 注意:这里直接返回函数,而不是函数的执行结果result = outer()result() # 11result() # 12result() # 13result() # 14result() # 15

闭包是如何保存外层变量的?

外层变量会被保存到 闭包单元(cell)中,例如下面的代码中,那些被inner函数所使用到的outer函数中的局部变量,会被封存在 闭包单元(cell)中,这些cell组成一个__closure元组,保存在了inner函数上。

defouter():    num = 10    print(id(num)) # 140723580982472definner():nonlocal num        num += 1        print(num)return inner # 注意:这里直接返回函数,而不是函数的执行结果result = outer()print(result.__closure__) # __closure__的值是一个元组,其中保存着被inner函数所“封存”的外层变量print(result.__closure__[0].cell_contents) # 10print(id(result.__closure__[0].cell_contents)) # 140723580982472print()result()print(result.__closure__) # __closure__的值是一个元组,其中保存着被inner函数所“封存”的外层变量print(result.__closure__[0].cell_contents) # 11print(id(result.__closure__[0].cell_contents)) # 140723580982504print()result()print(result.__closure__) # __closure__的值是一个元组,其中保存着被inner函数所“封存”的外层变量print(result.__closure__[0].cell_contents) # 12print(id(result.__closure__[0].cell_contents)) # 140723580982536

思考:内层函数inner会保存外层函数outer中所有的数据吗?不会,只会保存inner中所用到的。

defouter():    num = 10# print(id(num)) # 140723580982472    msg = 'hello'    print(msg)definner():nonlocal num        num += 1        print(num)        print(msg)return inner # 注意:这里直接返回函数,而不是函数的执行结果result = outer()# 验证方法:进行注释和不注释inner函数内 print(msg) 这行代码后,看下面的打印结果print(result.__closure__) 

注意点

1、每次获得一个新闭包,互补影响(闭包之间是互相独立的)。

defouter():    num = 10definner():nonlocal num        num += 1        print(num)return innerf1 = outer()f1() # 11f1() # 12f1() # 13print('-' * 10)f2 = outer()f2() # 11f2() # 12f2() # 13

2、外层变量为可变对象时,仍互不影响。

defouter2():    nums = []definner(value):nonlocal nums        nums.append(value)        print(nums)return innerf1 = outer2()f1(10) # [10]f1(20) # [10, 20]f1(30) # [10, 20, 30]print('-' * 10)f2 = outer2()f2(100) # [100]f2(200) # [100, 200]

闭包的优点

❏ 可以“记住”状态:不用全局变量,也不用写类,就能在多次调用之间保存数据。

❏ 可以做“配置过的函数”:先传一部分参数,把环境固定住,得到一个定制版函数。

❏ 可以实现简单的“数据隐藏”:外层变量对外不可见,只能通过内层函数访问。

❏ 是装饰器(decorator)等高级用法的基础。

小案例:使用闭包实现对文字的美化效果。

defbeauty(char, n):defshow_msg(msg):        print(char * n + msg + char * n)return show_msgshow1 = beauty('*', 3)show1('你好') # ***你好***show1('Python') # ***Python***show1 = beauty('@', 6)show1('你好') # @@@@@@你好@@@@@@show1('Python') # @@@@@@Python@@@@@@

闭包的缺点

❏ 理解成本较高:对初学者不太友好,滥用会让代码难读。

❏ 如果闭包里引用了很大的对象,又长期不释放,可能会增加内存占用。

❏ 很多场景下,其实用【类 + 实例属性】会更清晰,闭包不一定是最优解。

例如:将上述的文字美化效果的案例,用类实现:

classBeauty:def__init__(self, char, n):        self.char = char        self.n = ndefshow_msg(self, msg):        print(self.char * self.n + msg + self.char * self.n)b1 = Beauty('*', 3)b1.show_msg('Python') # ***Python***b2 = Beauty('@', 6)b2.show_msg('Python') # @@@@@@Python@@@@@@

最新文章

随机文章