Python编程中闭包的变量作用域问题解析

 更新时间:2021年10月21日 14:59:35   作者:晓鹏-King  
这篇文章主要介绍了Python编程中闭包的变量作用域问题解析,在学习Python的返回函数的时候,我发现里面涉及了几个问题,在这里为大家分享讲解下

闭包

​ 在我们使用返回函数的时候,由于我们在一个函数中需要返回另一个函数,因此,我们在这个函数中就需要重新定义一个函数。而这样,就造成了我们的函数嵌套问题。外面的函数相对于里面的函数而言是外函数(outer function),而里面的我们叫他内函数(inner function)。

def outerFunction(): #外函数
    def innerFunction(): #内函数
        x = 1
        return x
    return innerFunction #返回值是一个函数
a = outerFunction()
print(a)

这里我们打印 a 的值得时候,实际上打印的是我们的返回函数的地址 :

<function outerFunction.<locals>.innerFunction at 0x0000019C278C0E50>

​ 一般情况下,我们在使用函数作为返回值得时候,我们会在内函数中使用我们外函数中的变量,这种情况就会产生一个有意思的事情了。内函数会被返回给外部的其他调用者,而我们的变量是在我们的外函数中定义的。此时,我们的变量的作用域会发生怎样的变化呢?

测试:

def outerFunction(x): #外函数
    y = 10
    def innerFunction(): #内函数
        return x + y
    return innerFunction #返回值是一个函数
a = outerFunction(10)
print(a())

​打印:

20

​ 这里可以看到,我们的在给 a 赋值的时候,同时给外函数传进去了一个值10,然后,我们直接把 a() 打印出来,此时,我们的 a 返回了 20,说明我们的变量和参数在进入内函数后,我们的内函数会保留这个变量的值。

​ 这里,我们把这种现象叫做“闭包(Closure)”,我试着多次返回这个内函数,看看他们的地址是否一致:

def outerFunction(x): #外函数
    y = 10
    def innerFunction(): #内函数
        return x + y
    return innerFunction #返回值是一个函数
a = outerFunction(10)
b = outerFunction(20)
c = outerFunction(30)
print(a())
print(b())
print(c())
print(a)
print(b)
print(c)

​ 打印:

20
30
40
<function outerFunction.<locals>.innerFunction at 0x0000020C480C0DC0>
<function outerFunction.<locals>.innerFunction at 0x0000020C480C0D30>
<function outerFunction.<locals>.innerFunction at 0x0000020C480CD280>

​ 这里我们可以看到每个 innerFunction 的地址都不同,当我们多次调用返回函数的时候,每调用一次,我们的返回函数就会新创建一个函数给我们的变量。

​ 那么,如果我们在外函数里面多次调用我们的内函数,我们的外函数的变量的作用域是什么样的呢?

​ 我们测试一下:

def outerFunction(x): #外函数
    L=[] #定义一个List 
    y = 10
    for i in range(1, 4):
        def innerFunction(): #内函数
            return (x + y) * i #使用我们外函数得变量
        print(innerFunction()) #打印内函数返回得值
        #print(innerFunction)
        L.append(innerFunction) #把内函数添加到我们得List中
    return L #返回这个List
a = outerFunction(10)
print(a[0])
print(a[1])
print(a[2])
print(a[0]())
print(a[1]())
print(a[2]())

​ 看打印:

20
40
60
<function outerFunction.<locals>.innerFunction at 0x00000274AD6B0E50>
<function outerFunction.<locals>.innerFunction at 0x00000274AD6BD040>
<function outerFunction.<locals>.innerFunction at 0x00000274AD6BD3A0>
60
60
60

​ 这里我们可以看到一个有意思得现象:

​ 当我们在我们的函数内多次调用我们的内函数,并把它的返回值打印出来的时候,我们可以看到这个值是正常的(依次递增)。而如果我们把这个内函数传递到我们的 List 中,在函数外部调用它的时候,我们的函数的返回值全部变成了 60 也就是他们的只得到了我们外函数中变量的最终的值。这个就是我们的闭包特有的现象。

闭包中的变量

​ 一般情况下,当一个函数结束的时候,在内存中,我们这个函数内部的局部变量会连同这个函数一起被释放掉。

​ 但是,当我们这个函数包含闭包的时候,也就是说,当这个函数的返回值是一个函数的引用的时候。此时,当这个函数结束时,由于它内部的局部变量需要被释放掉,因此,它会把这个变量的值给传递给它所要返回的这个函数的内部。正是因为这样,当我们在外部调用我们的返回函数的时候,我们会看到它使用的变量全都是这个变量在函数内的最终的值,因为这个变量是这个外函数在结束的时候才传递给我们的返回函数的。而此时,我们函数内的变量只能有一个值。

​ 但是,我们可以使用另一个办法来规避这种情况:

def outerFunction(x): #外函数
    L=[] #定义一个List 
    y = 10
    def innerFunction(i): #内函数
        def f():
            return (x + y) * i #使用我们外函数得变量
        return f
    for i in range(1, 4):
        print(innerFunction(i)()) #打印内函数中的内函数的返回值
        L.append(innerFunction(i)) #把内函数添加到我们得List中
    return L #返回这个List
a = outerFunction(10)
print(a[0])
print(a[1])
print(a[2])
print(a[0]())
print(a[1]())
print(a[2]())

​ 打印如下:

20
40
60
<function outerFunction.<locals>.innerFunction.<locals>.f at 0x00000155B0F80040>
<function outerFunction.<locals>.innerFunction.<locals>.f at 0x00000155B0F803A0>
<function outerFunction.<locals>.innerFunction.<locals>.f at 0x00000155B0F80430>
20
40
60

​ 这里我们可以看到我们在外部调用的时候,函数的返回值和在内部调用的返回值是一样的。那么我们分析一下这个函数的执行过程以及函数内的变量的作用域情况。

​ 首先,我们在我们由于的内函数的基础又添加了一层内函数 f() ,并且,在这个内函数里面,我们使用了外部的内函数 innerFunction 的参数作为了返回值。然后,我们在 for 循环内部把我们的 innerFunction 这个函数的返回值添加进来我们的 List 里面。

​ 这里我们应该已经发现了。在 for 循环内部,当我们把 innerFunction 的返回值 f 添加到我们的 List 中的时候,由于 innerFunction 相对于函数 f() 而言,f() 是属于 innerFunction 的内函数的,因此,当我们返回 f() 这个函数的时候,此时,f() 函数内部使用的值就是它的最终值了,而此时,我们的 innerFunction 函数还在 for 循环内部,因此,在循环内部,每次调用我们的 innerFunction 的返回值时,我们的传递到我们的 f() 函数中的值就是我们的最终值了。

​ 此时,即使是我们在外部调用这个 f() 函数,它返回的值和内部调用时也是一样的。

以上就是Python编程中闭包的变量作用域问题解析的详细内容,更多关于Python闭包中变量作用域的资料请关注脚本之家其它相关文章!

相关文章

  • python中for循环把字符串或者字典添加到列表的方法

    python中for循环把字符串或者字典添加到列表的方法

    今天小编就为大家分享一篇python中for循环把字符串或者字典添加到列表的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • Qt通过QGraphicsview实现简单缩放及还原效果

    Qt通过QGraphicsview实现简单缩放及还原效果

    本文主要介绍通过QGraphicsview实现简单的缩放以及缩放后还原原始大小,通过scale可以对view进行放大或缩小,具体内容详情跟随小编一起看看吧
    2021-09-09
  • Python爬虫之Selenium警告框(弹窗)处理

    Python爬虫之Selenium警告框(弹窗)处理

    这篇文章主要介绍了Python爬虫之Selenium警告框(弹窗)处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • python Tornado事件循环示例源码解析

    python Tornado事件循环示例源码解析

    这篇文章主要为大家介绍了python Tornado事件循环示例源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • pandas改变df列的顺序的方法实现

    pandas改变df列的顺序的方法实现

    本文主要介绍了pandas改变df列的顺序的方法实现,主要使用 Pandas 中的 reindex() 方法,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-03-03
  • tensorflow:指定gpu 限制使用量百分比,设置最小使用量的实现

    tensorflow:指定gpu 限制使用量百分比,设置最小使用量的实现

    今天小编就为大家分享一篇tensorflow:指定gpu 限制使用量百分比,设置最小使用量的实现,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-02-02
  • 基于Python实现批量保存视频到本地

    基于Python实现批量保存视频到本地

    我们刷视频时常常会想把精彩的视频保存到本地,如果少数的还行,如果有很多的话一个个保存太麻烦了。本文教你如何用Python实现视频批量保存到本地,需要的可以参考一下
    2022-05-05
  • python实现嵌套列表平铺的两种方法

    python实现嵌套列表平铺的两种方法

    今天小编就为大家分享一篇python实现嵌套列表平铺的两种方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-11-11
  • Python常用扩展插件使用教程解析

    Python常用扩展插件使用教程解析

    这篇文章主要介绍了Python常用扩展插件使用教程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • Python 敏感词过滤的实现示例

    Python 敏感词过滤的实现示例

    本文主要介绍了Python 敏感词过滤的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08

最新评论