python的闭包
python的赋值和修改变量
在理解闭包之前我们需要先理解什么是赋值和修改
在Python中,重新赋值(reassignment)和修改变量(modifying a variable)通常指的是两种不同的操作,尤其是在处理不可变(immutable)和可变(mutable)数据类型时。
重新赋值:
重新赋值是指将一个新的值赋给一个已经存在的变量名。这个操作会改变变量名所引用的内存地址。
x = 5 # x 引用了一个整数对象,值为5
x = 'hello' # 现在x被重新赋值为一个字符串对象 'hello'
在这个例子中,变量x最初指向一个整数对象5。当我们给x赋一个新的值,如字符串’hello’时,我们就进行了重新赋值。x现在指向一个全新的内存地址,即一个字符串对象。
修改变量:
修改变量通常是指改变一个可变数据类型的内容,而不改变变量名所引用的内存地址。这种情况通常发生在处理列表(list)、字典(dict)等可变数据类型时。
y = [1, 2, 3] # y 引用了一个列表对象
y.append(4) # 我们修改了y引用的列表对象,现在y是[1, 2, 3, 4]
在这个例子中,变量y指向一个列表对象。使用append()方法在列表末尾添加一个元素并没有改变y的引用地址,而是修改了y所指向的列表对象本身。
总结:
重新赋值改变了变量的引用,即变量名指向内存中的另一个位置。
修改变量改变了变量所指向的对象的内容,这仅适用于可变数据类型。
对于不可变数据类型(如整数、字符串、元组等),任何看似“修改”其值的操作实际上都是在进行重新赋值,因为不可变对象一旦创建就不能被改变。对于可变数据类型(如列表、字典、集合等),则可以在不改变其引用的情况下修改其内容。
闭包的简单模型
def f():
data = []
def inner(value):
data.append(value)
return data
return inner
g = f()
print(g(1))
print(g(2))
执行结果
[1]
[1, 2]
1、首先我们定一个函数f
2、在函数f里我们创建了一个局部变量data,是一个空的数组,记住,是局部变量
3、然后在f的内部我们又定义了一个函数inner
4、这个inner内部呢,是对f的局部变量data进行操作的,并将其返回
5、f呢,它的返回时inner这个函数,记住,是这个函数,记住不要用inner()
6、所以g=f(),返回的就是inner这个函数
7、g(1)就是inner(1),就在data中append了一个1
那这么一种能从一个函数里读取其他函数变量的机制,就叫闭包。
闭包是简单的复制么
那大家怎么理解这个事儿呢,这个顺序又是什么呢。
第一个理解,就是inner直接把f的局部变量复制到了inner里面,那我们这么尝试一下
# -*- encoding: utf-8 -*-
"""
@File : test1.py
@Modify Time @Author @Version
------------ ------- --------
2024/3/26 17:03 zhangyin 1.0
# @Description:
"""
def f():
data = []
def inner(value):
data.append(value)
return data
data = [0]
return inner
g = f()
print(g(1))
print(g(2))
结果
[0, 1]
[0, 1, 2]
这一次,我们在inner的内部,新增了一条data=[0],看好了,我们做的是赋值,并不是修改了data这个变量,如果inner是将data复制进了inner内部,那这一次重新赋值,不应该影响到inner里的data,但结果说明,还是影响到了inner里的data,说明不是简单的复制。
接下来我们看一下这里面的顺序。
# -*- encoding: utf-8 -*-
"""
@File : test1.py
@Modify Time @Author @Version
------------ ------- --------
2024/3/26 17:03 zhangyin 1.0
# @Description:
"""
def f():
data = []
print(f"第一次打印的{data}")
def inner(value):
data.append(value)
return data
data = [0]
print(f"第二次打印的{data}")
return inner
g = f()
print("下面是函数的执行")
print(g(1))
print(g(2))
结果
第一次打印的[]
第二次打印的[0]
下面是函数的执行
[0, 1]
[0, 1, 2]
说明除了这个inner之外,在g=f()调用方法f的时候,f函数体里的代码都已经执行完了。
这个data到底存在了哪里?
我们知道,一个函数是不会引用它的局部变量的,只有在函数运行的时候才回去创建这个变量,在运行完之后,这个局部变量就被丢弃了。那data是f的局部变量,在我们调用f之后,f这个函数的对data的引用就应该失效了,那凭什么我们在反复调用g的时候,这个data还一直被保存在内存中呢,形象点的解释就是在编译期间由编译器决定这个是单纯的局部变量还是闭包,如果是闭包的,会放在一个盒子里,大家都从这里取,等被调用的时候,这个data的值,被一个cell引用,如果修改了这个盒子里的值,会将这个值从盒子里扔出去,再将新的放进来。但是我们知道python是一个引用计数的语言,那到底是谁在引用盒子里的cell,然后通过cell拿到data呢,或者说cell活在哪里了,答案是它保存在了
g=f()
g就是一个function object,它保存在了这里面,所以只要g还活着,那这个data就一直存在
闭包有什么意义
这个f函数,就成了一个小全局,一个介于全局和非全局的状态。