社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

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

梦想橡皮擦 • 4 年前 • 447 次点击  
阅读 101

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

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

八、闭包的知识点

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

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 相当于内层函数,并且保留了自由变量 params
inner = 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 这篇博客的总结

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

博主 ID:梦想橡皮擦,希望大家点赞、评论、收藏。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/115992
 
447 次点击