基本 Python 入门笔记:闭包
闭包
通过解决需求问题来了解闭包。
这个要求是:我们必须继续以分钟为单位记录我们的学习时间。例如,如果我学习 2 分钟,我就回馈 2 分钟。如果我学习一段时间后10分钟,我就会回馈12分钟。学习时间累计如下。
面对这个问题,我们通常会创建一个全局变量来记录时间,然后用一种方法将每次的学习时间相加,通常写成如下形式:
time = 0
def insert_time(min):
time = time + min
return time
print(insert_time(2))
print(insert_time(10))
复制代码
其实Python中就是这样做的。报告错误。报出如下错误: UnboundLocalError:局部变量 'time' 在赋值之前被引用
这是因为,在 Python 中,如果一个函数使用了与全局变量相同的名称和该变量的值,那么该变量变成了局部变量,这会导致我们在函数中引用它而没有定义它,所以报这个错误。
我们可以使用global关键字,具体变化如下:
time = 0
def insert_time(min):
global time
time = time + min
return time
print(insert_time(2))
print(insert_time(10))
复制代码
输出结果如下:
2
12
复制代码
但是这里使用的是全局变量。在开发过程中我们应该尽可能避免使用全局变量。由于不同的模块和不同的函数可以自由访问全局变量,因此全局变量可能是不可预测的。例如,程序员A更改了全局变量time的值,然后程序员B也更改了时间。如果存在错误,则很难发现和纠正此类错误。
我们目前正在使用闭包来解决该问题。首先直接看代码:
time = 0
def study_time(time):
def insert_time(min):
nonlocal time
time = time + min
return time
return insert_time
f = study_time(time)
print(f(2))
print(time)
print(f(10))
print(time)
复制代码
输出结果如下:
2
0
12
0
复制代码
这里最直接的体现就是全局变量time至今没有改变,这里仍然使用non-local。指示在函数或其他作用域中使用外部(非全局)变量的关键字。那么上面代码的具体运行过程是怎样的呢。我们可以看下图:
外部函数局部作用域中的变量在内部函数局部作用域中可访问的行为称为:闭包。 更直接的表达方式是,当函数作为对象返回时,包含外部变量,这些变量形成闭包。
有没有办法验证这个功能是否关闭?
是的,所有函数都有一个属性__closure__
。如果该函数是一个闭包,它将返回一个由单元格组成的元组对象。cell 对象的 cell_contents 属性是存储在闭包中的变量。看代码:
ime = 0
def study_time(time):
def insert_time(min):
nonlocal time
time = time + min
return time
return insert_time
f = study_time(time)
print(f.__closure__)
print(f(2))
print(time)
print(f.__closure__[0].cell_contents)
print(f(10))
print(time)
print(f.__closure__[0].cell_contents)
复制代码
打印的结果是:
(<cell at 0x0000000000410C48: int object at 0x000000001D6AB420>,)
2
0
2
12
0
12
复制代码
打印的结果显示,传递的值始终存储在闭包的cell_contents中。所以,这就是闭包的最大特点。您可以将父函数的变量与内部定义的函数绑定组合起来。即使生成闭包的父函数被释放,闭包仍然存在。
闭包过程基本上就像类(父函数)生成实例(闭包)。不同的是,父函数只有在调用时才执行,执行后环境就释放,而类是在文件执行时创建的。一般情况下,作用域是在程序执行后释放的。因此,对于一些需要重用且不足以定义为类的行为的函数使用闭包会比使用类占用更少的资源,并且更轻量、更灵活。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。