11、Python 闭包实现原理

本文介绍了内存的逻辑状态,包括代码区、静态数据区和动态数据区,重点讲解了栈和堆的区别。接着,文章深入探讨了闭包的概念,通过一个示例解释了闭包如何在函数内部引用外部变量,以及这种引用如何影响内存管理。在闭包的示例中,详细阐述了内外函数的关系和内存中的对象生命周期。最后,分析了闭包在内存中的持久化现象,以及如何避免内存泄漏。
摘要由CSDN通过智能技术生成

一、内存的逻辑状态

在了解闭包之前,让我们简单了解下内存逻辑状态。内存空间在逻辑上分为三部分:代码区、静态数据区和动态数据区,动态数据区又分为栈区和堆区

  • 栈内存:稳定性差,用于临时存储数据,存储数据比较快。方法中定义的变量都存放在这个区域,但存放的只是名称和指向堆内存空间的地址
  • 堆内存:稳定性高,用于长期存储数据,存储数据的速度慢。程序运行时会创建对像,对象会被保存堆内存中,以便反复利用。
  • 常量区:可以永久存储软件中经常使用的固定数据
  • 资源区: 可以存储程度运行的基本资源数据,如:运行的二进制代码

下图展示了基本的变量在内存中的逻辑关系:
在这里插入图片描述

说明:堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。

二、闭包实现原理

有了以上的基础,我们现在对闭包进行详细的了解,闭包的本质是一个函数,而这个函数引用了上层局部命名空间的变量。

自由变量:未在本地作用域中定义的变量,例如:定义外层函数内被内层引用的变量
闭包:是一个概念,出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包,很多语言中都有这个概念,在Python中常见于装饰器中

让我们来看个例子:

def outer():
    c = [1]
    def inner():
        c[0] =+ 1  #这一行会报错吗?
        return c[0]
    return inner

foo = outer()
foo()
foo()

运行结果:
在这里插入图片描述
为什么会出现以上结果,这是因为outer函数的执行结束后,outer的引用为0,刚释放了相应的堆栈空间,但C[0]仍被内层的inner引用,这时C[0]就r被保留了在了栈内,当Inner被调用时,便可以使用c[0]的值

我们可以看粗略的看下闭包的图示过程,但下图仅供了解基本原理:

  1. 程序运行时,解释器会将代码以二进制形式加载到代码区,当我们执行到foo = outer()时,调用函数,从代码区找到内容,并在堆区创建Outer的堆空间,即栈针
    在这里插入图片描述

2.然后outer函数返回,foo变量名指向了内部inner函数。这时虽然Outer的运行空间被释放。
但由于函数加载的时候inner内使用了C[0],这样的话,在outer释放的时候,将c[0]与Inner进行了绑定,也就是说,C的引用计数不为0,这样它在堆栈内就未被释放 ,从而当我们在inner执行多次时,C[0]的值一直可以被使用,直到c[0]使用完成,引用计数为0被释放

在这里插入图片描述

以上内容均为个人学习理解,如有不正确,还请指正,如果有转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值