Python编程中闭包的变量作用域问题解析
作者:晓鹏-King 发布时间:2023-07-27 01:59:47
闭包
在我们使用返回函数的时候,由于我们在一个函数中需要返回另一个函数,因此,我们在这个函数中就需要重新定义一个函数。而这样,就造成了我们的函数嵌套问题。外面的函数相对于里面的函数而言是外函数(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闭包中变量作用域的资料请关注脚本之家其它相关文章!
来源:https://blog.csdn.net/qq_25827755/article/details/120859552


猜你喜欢
- 1.筛选出目标值所在行 单列筛选# df[列名].isin([目标值])对当前列中存在目标值的行会返回True,不存在的返回Fal
- 在正文前,先简短介绍自己。我任职于广州的某个网站服务公司的系统开发员,主要任务是以.Net编写各种web系统,例如CMS.EIP。大家都知道
- 一、简单使用入门小案例import logginglogging.basicConfig(level=logging.DEBUG, &nbs
- 1、enumerate返回针对序列类型的可迭代对象的枚举对象。2、eval取出字符串中的内容。将str中有效的表达式返回计算结果。3、exe
- 今天继续给大家介绍Python相关知识,本文主要内容是Python asyncio异步编程常见问题。一、asyncio编程简单示例首先,我们
- 背景我们经常调侃程序员每天都在写bug,这确实是事实,没有测出bug不代表程序就真的不存在问题。传统的代码review、静态分析、人工测试和
- Access爱好者以会VBa为荣。我觉得这不是好现象。vba只是vb的子集,有着很多限制,比如不支持继承,不支持指针,不支持子界类型等。使用
- 注:代码用 jupyter notebook跑的,分割线线上为代码,分割线下为运行结果1.导入库生成缺失值通过pandas生成一个6行4列的
- 引言随着圣诞的到来,大家纷纷@官方微信给自己的头像加上一顶圣诞帽。当然这种事情用很多P图软件都可以做到。但是作为一个学习图像处理的技术人,还
- 一、为什么难 秒杀系统难做的原因:库存只有一份,所有人会在集中的时间读和写这些数据。例如小米手
- 自定义组件挂载原型上以elementUI二次分装dialog举例PageDialog.vue<!-- 页面提示弹框 --><
- 目录什么是引用计数怎么查看引用计数?对象的引用计数数组的引用计数关于内存泄露需要注意的地方总结什么是引用计数在PHP的数据结构中,引用计数就
- 必要准备你得有一个sqlserver数据库,并且要和vs项目连接。关于VS连接sqlserver数据库的教程前几天发过了,链接如下VS202
- 本文以实例形式较为详细的讲解了Python的多线程,是Python程序设计中非常重要的知识点。分享给大家供大家参考之用。具体方法如下:用过P
- 如下所示:import numpy as npa = np.array([[1,2,3],[4,5,6],[7,8,9]])b = np.a
- 前言相信大家都知道当声明一个变量,并且没有给赋值的情况下,它的初始值是undefined。但是在javascript中,怎么检查一个值是否为
- 1.函数就是对象,而函数名是指向函数对象的指针,不会与某个函数绑定。 2.函数没有重载(函数重载:同一个函数名对应着多个函数的实现.) Fo
- <?phpfunction map($fun, $list,$params=array()){ $
- 1。在Asp页面首部<head>加入 Response.Buffer =
- MVC和MTV框架MVCWeb服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们