闭包函数的部分思考

由 avimitin 发布

今天在廖雪峰博客里学了个返回函数,然后在闭包函数那里卡住了。

闭包变量引用

首先关于这一部分,教程里没有详细代码说明,这里post一个解释很详细的博客:python中闭包详解

我在这里简单讲一下我的理解:

def aa():
    x = 0
    
    def bb():
        x = 1
        return x
    return counter

aa函数里的x变量,是属于aa()定义域里的变量,这里的x指向了0这个int对象。而在bb()函数里的x变量,是bb()定义域里的变量,这里的x则指向了1这个int对象。

所以在bb()里写x = 1只在bb()里引用,不会对aa()里的x起作用。


那么问题来了,廖雪峰讲的引用外部函数的局部变量是什么意思呢。其实是把aa()定义域里的变量对象指过来。可以拿到aa()定义域里x指向的对象,但是不能改变aa()定义域里x指向的对象。

简而言之,就是你可以拿到x的值,但是不能改变他的值。

所以假如要直接使用的话,只能使用x的值。

但是也不是不能在内部方法里改变外部方法的值,有两种办法:

  • 使用一个可变参数,如字典,列表,使用append方法和pop方法增删值
  • 在内部方法使用nonlocal关键字声明这个变量指向非本地对象,然后变量就会向上查询对象。

具体使用方法:

def cc():
    x = 0
    y = [1, 2]
    
    def dd():
        nonlocal x
        x += 1
        y.append(3)
        return x, y
    return dd


cc()
ee = cc()
print(ee())

这个程序执行完之后会输出 (1, [1,2,3])

闭包到底是啥

一般的Python程序,在方法里进行赋值操作之后,内存会开辟一个空间存放变量指向的对象,方法执行完毕之后清除内存。而使用闭包的方法,内部函数假如引用了外部函数的局部变量,这个外部函数的变量会与内部函数捆绑,不会释放,直到整个程序结束。举个例子:

def counter():
    x = 0
    # 这里检测counter()作用域中x的值
    print(x, end=',')
    # 改变一下x的值
    x += 1
    print(x, end=',')

    def count_counter():
        nonlocal x
        x += 1
        # 这里检测count_counter()作用域中x的值
        print(x, end=',')
        return x

    return count_counter

# 连续三遍执行程序,查看输出的值
a = counter()
# 先执行三遍counter()
counter(), counter(), counter()
# 再执行三遍count_counter()
a(), a(), a()

这个程序最后会输出:

0,1,0,1,0,1,0,1,2,3,4,

前半部分的3个0,1,就是单独调用counter()时的输出。可以看到每次执行完之后,内存都释放了,每次调用都重新从内存开辟空间,所以会先赋值0,然后再+1。而当调用内部函数时,外部函数的变量绑定到内部函数里,于是每次调用都不会重新开辟空间。

用面向对象的方法来思考会简单多一点,上面的解释是建立在没学过类的大白话解释。


所以闭包就是在函数内封装一个变量,然后供内部函数反复调用,因为外部函数返回内部函数名,看起来像封装起来了,所以叫闭包。


2 条评论

  1. 王木木
    王木木 · 2020-08-03 22:59

    放心吧,闭包这个东西以后99.999的概率用不上,java 还支持闭包写法但是有几个人听说过

    1. avimitin
      avimitin · 2020-08-10 21:18 作者

      当时看着黑马学的,所以就((

发表评论