写点什么

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

发布于: 2021 年 03 月 12 日
滚雪球学 Python 之闭包操作,本系列第 8 篇文章

橡皮擦,一个逗趣的互联网高级网虫,新的系列,让我们一起 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("内")
复制代码


对上述代码的说明如下,在调用 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 相当于内层函数,并且保留了自由变量 paramsinner = func("外")inner("内")
复制代码


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


  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")
复制代码


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


8.3 闭包的作用


再上文中,你是否已经对闭包的作用有初步了解了?

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


def outer_func():
msg = "梦想橡皮擦" def inner_func(): print(msg)
return inner_func
outer = outer_func()outer()
复制代码


如果你对滚雪球第一遍还有印象,会了解到局部变量仅在函数的执行期间可用,也就说 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__)
复制代码


(<cell at 0x0000000002806D68: str object at 0x0000000001D46718>,)
复制代码


返回的元组中,第一项是 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())
复制代码


上述代码不是简单的返回了一个闭包函数,而是返回的一个包含三个闭包函数的序列 list

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


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


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


8.6 这篇博客的总结

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


相关阅读


  1. Python 爬虫 100 例教程,超棒的爬虫教程,立即订阅吧

  2. Python 爬虫小课,精彩 9 讲





博主 ID:梦想橡皮擦,希望大家<font color="red">点赞</font>、<font color="red">评论</font>、<font color="red">收藏</font>。


发布于: 2021 年 03 月 12 日阅读数: 9
用户头像

爬虫 100 例作者,蓝桥签约作者,博客专家 2021.02.06 加入

6 年产品经理+教学经验,3 年互联网项目管理经验; 互联网资深爱好者; 沉迷各种技术无法自拔,导致年龄被困在 25 岁; CSDN 爬虫 100 例作者。 个人公众号“梦想橡皮擦”。

评论

发布
暂无评论
滚雪球学 Python 之闭包操作,本系列第 8 篇文章