滚雪球学 Python 之闭包操作,本系列第 8 篇文章

举报
梦想橡皮擦 发表于 2021/05/26 16:08:13 2021/05/26
【摘要】 橡皮擦,一个逗趣的互联网高级网虫,新的系列,让我们一起 Be More Pythonic。 滚雪球学 Python 第二轮 已完成的文章清单 八、闭包的知识点8.1 闭包的基本操作8.2 闭包作用域8.3 闭包的作用8.4 判断闭包函数8.5 闭包存在的问题8.6 这篇博客的总结 已完成的文章清单 滚雪球学 Python 第二轮开启,...

橡皮擦,一个逗趣的互联网高级网虫,新的系列,让我们一起 Be More Pythonic

已完成的文章清单

  1. 滚雪球学 Python 第二轮开启,进阶之路,列表与元组那些事儿
  2. 说完列表说字典,说完字典说集合,滚雪球学 Python
  3. 关于 Python 中的字符串,我在补充两点,滚雪球学 Python
  4. 列表推导式与字典推导式,滚雪球学 Python
  5. 滚雪球学 Python 之 lambda 表达式
  6. 滚雪球学 Python 之内置函数:filter、map、reduce、zip、enumerate
  7. Python 中级知识之装饰器,滚雪球学 Python

八、闭包的知识点

闭包,又叫做闭包函数、闭合函数,写法类似函数嵌套。

8.1 闭包的基本操作

从复杂的概念中抽离出来,在 Python 中,闭包就是你调用一个函数 X,这个函数返回一个 Y 函数给你,这个返回的函数 Y 就是闭包。

掌握任何技术前,都要先看一下最基本的案例代码:

def func(parmas): # 内部函数 def inner_func(p): print(f"外部函数参数{parmas},内部函数参数{p}") return inner_func


inner = func("外")
inner("内")

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

对上述代码的说明如下,在调用 func("外") 的时候产生了一个闭包 inner_func 函数,该闭包函数内调用了外部函数 func 的参数 parmas,此时的 parmas 参数被称为自由变量(概念性名词,了解即可)。当函数 func 的声明周期结束后,parmas 这个变量依然存在,原因就是被闭包函数 inner_func 调用了,所以不会被回收。

简单的进行闭包操作学习之后,你会发现闭包操作,在上篇博客已经使用过了,博客的说明的内容是装饰器

再次对上文代码进行注释,帮助你理解闭包函数的实现。

# 定义外部(外层)函数
def func(parmas): # 定义内部(内层)函数 def inner_func(p): print(f"外部函数参数{parmas},内部函数参数{p}")
	# 一定要返回内层函数 return inner_func

# 调用外层函数,赋值给一个新变量 inner,此时的 inner 相当于内层函数,并且保留了自由变量 params
inner = func("外")
inner("内")

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

总结下来,实现一个闭包需要以下几步:

  1. 必须有一个内层函数 ;
  2. 内层函数必须使用外层函数的变量,不使用外层函数的变量,闭包毫无意义;
  3. 外层函数的返回值必须是内层函数。

8.2 闭包作用域

先看代码:


def outer_func(): my_list = [] def inner_func(x): my_list.append(len(my_list)+1) print(f"{x}-my_list:{my_list}") return inner_func


test1 = outer_func()
test1("i1")
test1("i1")
test1("i1")
test1("i1")

test2 = outer_func()
test2("i2")
test2("i2")
test2("i2")
test2("i2")

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

上述代码中的自由变量 my_list 的作用域,只跟每次调用外层函数,生成的变量有关,闭包每个实例引用的变量互相不存在干扰。

8.3 闭包的作用

再上文中,你是否已经对闭包的作用有初步了解了?
接下来再强调一下,闭包操作中会涉及作用域相关问题,最终实现的目标是脱离了函数本身的作用范围,局部变量还可以被访问到。

def outer_func(): msg = "梦想橡皮擦" def inner_func(): print(msg) return inner_func

outer = outer_func()
outer()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

如果你对滚雪球第一遍还有印象,会了解到局部变量仅在函数的执行期间可用,也就说 outer_func 函数执行过之后,msg 变量就不可用了,但是上面执行了 outer_func 之后,再调用 outer 的时候,msg 变量也被输出了,这就是闭包的作用,闭包实现了局部变量可以在函数外部访问。
相应的理论再扩展一下,就是在该种情况下可以把局部变量当做全局变量用。

最后再备注一句,说明一下闭包的作用吧:闭包,保存了一些非全局变量,即保存局部信息不被销毁。

8.4 判断闭包函数

通过 函数名.__closure__ 判断一个函数是否是闭包函数。

def outer_func(): msg = "梦想橡皮擦" def inner_func(): print(msg) return inner_func

outer = outer_func()
outer()
print(outer.__closure__)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
(<cell at 0x0000000002806D68: str object at 0x0000000001D46718>,)

  
 
  • 1

返回的元组中,第一项是 CELL 即为闭包函数。

8.5 闭包存在的问题

这个问题是地址和值的问题,是操作系统底层原理导致的问题,具体实现先看代码,一个非常经典的案例。

def count(): fs = [] for i in range(1, 4): def f(): return i fs.append(f) return fs

f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

上述代码不是简单的返回了一个闭包函数,而是返回的一个包含三个闭包函数的序列 list
运行代码,输出 3 个 3,学过引用和值相关知识同学会比较容易掌握,上述代码中的 i 指向的是一个地址,而不是具体的值,这就导致当循环结束之后,i 指向那个地址的值等于 3

本案例你记住下面这句话也可。

尽量避免在闭包中引用循环变量,或者后续会发生变化的变量。

8.6 这篇博客的总结

本篇博客为大家补充了一下闭包相关的基础知识,配合上一篇装饰器博客一起学习,效果更加。

相关阅读

  1. Python 爬虫 100 例教程,超棒的爬虫教程,立即订阅吧
  2. Python 爬虫小课,精彩 9 讲

今天是持续写作的第 104 / 200 天。
如果你想跟博主建立亲密关系,可以关注同名公众号 梦想橡皮擦,近距离接触一个逗趣的互联网高级网虫。
博主 ID:梦想橡皮擦,希望大家点赞评论收藏

文章来源: dream.blog.csdn.net,作者:梦想橡皮擦,版权归原作者所有,如需转载,请联系作者。

原文链接:dream.blog.csdn.net/article/details/114413786

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。