python
闭包
- 在函数A的内部再定义一个函数B,并且函数B引用了函数A的变量,这种结构叫做闭包,函数B叫做闭包函数
def count1(): x = 0 def f(): nonlocal x x = x+1 # 对外部变量 x 进行了修改 return x return f def count2(): x = 0 def f(): nonlocal x return x+1 # 没有修改外部变量 x return f # 对count1() 和count2()每个均调用5次,count1()输出结果为5,count2输出结果为1 # 主要在于count1()在的内部函数引用了外部变量x并且进行了修改,而count2()则是直接引用了变量x并没有进行修改
- 在 Python 中,闭包函数确实记住了其外部作用域中的变量(或者说记住了外部命名空间),但这种记忆是通过引用来实现的。所以当闭包被创建时,它不是复制变量
i的值,而是保持对变量i的引用。由于 Python 中的变量是名字到对象的绑定,所以闭包函数记住的实际上是变量名i而不是其具体的内存地址。然后当闭包被调用时,Python 解释器会查找这个名字i当前所引用的对象,这也是为什么闭包可能反映外部作用域中变量的最新状态,因为它使用的是变量名来获取当前的值。
- 闭包意味着内部函数 f 记住了它从外部作用域(count 函数的作用域)中引用的变量 i 的值,而不是该变量某一时刻的副本。但是,由于 Python 的延迟绑定行为,这些内部函数实际上并没有在创建时立即计算出 i 的值,而是在调用时才去查找 i 的值。
- 理解闭包时,关键是要知道闭包引用的是变量本身而不是变量的值。这样就可以解释为什么即使闭包在循环中创建,但直到闭包被调用时才会求得真正的值——这个值是根据闭包引用的那个变量在闭包调用时刻所具有的值。
外部函数运行时闭包在内存中的变化
def outer_function(A_data): # 函数A中创建一些数据 A_local_data = 'local data in A' # 定义闭包函数B def inner_function(B_data): # 函数B可以访问外围函数A的局部变量 print(A_local_data) print(B_data) # 函数A返回闭包函数B return inner_function # 创建闭包实例 closure_instance = outer_function('data for A')
函数 A 的运行过程以及闭包函数 B 在内存中的变化
- 函数 A 运行之前
在外部函数
outer_function 被调用之前,它仅仅是在内存中定义好的一个函数对象。此时,还没有任何闭包被创建。因此,内存中还没有与闭包相关的任何内容。- 函数 A 运行中
当
outer_function 被调用时 (closure_instance = outer_function('data for A')),函数 A 开始执行,并且会为其局部变量 A_local_data 分配内存和存储数据 'local data in A'。接着,函数 A 中定义了一个闭包函数 B,即 inner_function。此时,在函数 A 的上下文环境中,闭包函数 B 被创建。因为闭包函数 B 会引用到函数 A 中的局部变量
A_local_data,这个变量会被包含在闭包的环境中。该过程称为词法闭包或者静态作用域,表示函数 B 记住了它被定义时的环境。- 函数 A 运行结束
当外部函数
outer_function 执行完毕并返回 inner_function 时,通常情况下,函数 A 的局部变量在函数退出时会被销毁。但由于有闭包存在,A_local_data 不会被销毁;相反,它将继续存在因为闭包函数 B 引用了它。这意味着 inner_function 继承了它被定义时的环境,保留了对 A_local_data 的引用。外部函数
outer_function 返回闭包函数 B 的引用,并且这个引用被赋值给变量 closure_instance。尽管外部函数 A 的执行已经完成,但是闭包函数 B 内部仍然可以访问 outer_function 的局部变量 A_local_data。在这个过程中,“闭包”这个术语指的是函数 B 及其周围状态(即函数 A 中的局部变量)。闭包允许你保存这些状态,即使外部函数的执行已经完成。
如果你现在调用
closure_instance('data for B'):python复制代码 closure_instance('data for B')
闭包函数 B 会打印出 'local data in A' 和 'data for B',证明虽然外部函数 A 已经执行结束,闭包函数 B 仍然持有并能够访问
A_local_data。所以在内存中,A_local_data 仍被闭包引用,直到闭包自身不再被需要并被垃圾回收器回收。