Pyhton入门12——闭包、作用、闭包延迟

闭包是指延伸了作用域的函数,其中包含函数定义体中的引用和不在定义体中定义的非全局变量。它的特别之处是能够访问定义体之外的非全局变量。这就需要使用内部函数和外部函数了。

1、闭包的条件

(1)在外部函数中定义了内部函数;

(2)外部函数的返回值是内部函数名;

(3)内部函数引用了外部函数的临时变量;

下面是一个闭包的例子,内部函数引用了外部函数的变量,并且外部函数的返回值是内部函数名。然后我们调用外部函数,并用 f 接收外部函数的返回值,可以看出此时 f 指向的地址就是内部函数的地址,也就可以通过 f 调用内部函数。

def outer_fun():
    a = 100

    def inner_fun():
        b = 200
        print(a, b)

    return inner_fun


f = outer_fun()
print(f)   # <function outer_fun.<locals>.inner_fun at 0x0000020A35B25F70>
f()   # 100  200

2、闭包的例子

下面的代码,直接运行是会报错的,原因是加 “ * ” 的代码出现错误,原因是对于内部函数来讲,num01是全局变量,而内部函数中定义的变量 num02 是局部变量,它的作用域仅限于内部函数,因此不能在外部函数中调用它。

去掉错误代码行,程序就可以正确运行,输出的结果是100、300。首先调用外部函数是,会执行外部函数函数体,此时只是加载内部函数到内存中,并没有执行内部函数,最终返回内部函数的引用。

def outer_fun():
    num01 = 100

    def inner_fun():
        num02 = 200
        print(num01 + num02)

    print(num01)
    print(num02)       # *
    return inner_fun


f = outer_fun()
f()

3、闭包的作用

(1)闭包能够保存返回外部函数变量的状态;

(2)闭包能够读取其他函数的内部变量;

(3)闭包延长了函数的作用域;

下面这个代码的输出结果如图所示,调用闭包了三次,输出的结果都是一样的,这是因为在调用了外部函数之后,外部函数执行完毕,此时变量 i 的值为 8 ,外部函数执行完毕,变量 i 对于内部函数来说就相当于全局变量,在每一次调用闭包时都有 i=8,所以三次的输出结果是一样的。

def outer_fun():
    for i in range(9):
        print(i, end=' ')

    print()

    def inner_fun(num):
        print(i * num,end=' ')

    return inner_fun


f = outer_fun()
for i in range(3):
    f(3)

4、闭包的缺点

(1)由于延长了函数的作用域,就会使得作用域不够直观;

(2)闭包中的变量不会被垃圾回收,所以存在一直占用内存的问题;

5、闭包的延迟

在闭包中,外部函数返回了内部函数的引用,当外部函数调用结束,内部函数才会保存调用的外部函数的变量,即闭包变量,此时,闭包变量对于内部函数来说相当于全局变量。

闭包延迟是指,只有在调用内部函数时,才会访问闭包变量,不调用时不访问闭包变量。

来看一下非常流行的闭包案例,该案例与匿名函数相结合,需要注意的是,闭包与是不是匿名函数没有关系,关键在于它是否能够访问函数体之外定义的非全局变量。:

def fun():
    tmp = [lambda x: i * x for i in range(4)]
    return tmp

for t in fun():
    print(t(2), end=' ')

上面这个例子的输出结果是 6,6,6,6

造成这样的输出结果的原因在于,内部函数引用了外部函数一个循环的局部变量,此时这个循环的局部变量就是闭包变量。根据闭包延迟的概念,不调用内部函数时,是不访问闭包变量的,外部函数执行结束,循环也就执行结束,当再调用内部函数时,此时内部函数访问的是循环执行结束之后闭包变量的值。在上面这个例子中,循环结束之后 i=3,而外部函数执行结束,返回的是一个包含了四个内部函数引用的列表,然后在列表中,调用内部函数,并传入参数,得到的结果就是 6,6 ,6,6。

 这个经典的案例,可以被写成下面这种形式,代码可读性较高,方便理解。

def func():
    lst = []

    for i in range(4):
        def inner_func(num):
            return i * num
        lst.append(inner_func)

    print(lst)
    return lst


temp = []
for f in func():
    temp.append(f(2))

print(*temp)

输出结果如下图所示,可以清楚的看出,列表 lst 保存了四个内部函数的引用,执行结果与上面的一样:

总结:

(1)可以发现,闭包能够完成之前需要类对象完成的工作,优化了变量;

(2)由于闭包引用了外部函数的局部变量,使得外部函数的局部变量没有及时释放,消耗了内存;

(3)闭包使得代码变得更加的简洁。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值