前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 编程 深入了解内存管理机制、深拷贝与浅拷贝

Python 编程 深入了解内存管理机制、深拷贝与浅拷贝

作者头像
叶庭云
发布2024-05-25 08:08:41
1320
发布2024-05-25 08:08:41
举报
文章被收录于专栏:Python进阶之路Python进阶之路

一、对象和引用、内存管理机制

Python 中的一切都是对象,包括数字、字符串、列表和函数等。为了简化内存管理并提高效率,Python 采用了统一的对象模型。在这个模型中,所有创建的变量在栈上都是指向堆中实际分配内存对象的引用。这种机制允许变量指向不同类型的对象,并可以在程序运行期间随时改变所指向的对象。这种设计使得 Python 的内存使用更加高效和灵活。对象还分两类:一类是可变的,一类是不可变的。
  • 不可变对象类型有:整数、浮点数、布尔值、字符串、元组等
  • 可变对象类型有:列表、字典、集合、可变的字节数组、用户自定义类

Python 的内存管理机制,包括引用计数、垃圾回收和内存池机制,是以对象引用为基础的。通过妥善管理对象引用,Python 能够高效地管理内存使用并回收不再使用的对象。

  • 内存池机制和垃圾回收是 Python 内存管理机制的两个主要组成部分,其目标是减少内存碎片化和提高程序执行效率。Python 的内存池机制是 Python 解释器用于提高内存分配效率的一种机制。具体而言,这一机制通过预先在内存中申请一定数量的、大小相等的内存块来实现。当 Python 程序运行时需要分配内存给小对象时,它会首先尝试从内存池中分配内存,而不是直接向操作系统请求。这样做可以减少频繁申请和释放内存所造成的内存碎片,从而提高内存使用效率。Python 的内存池主要管理的是小于 256KB 的对象,大对象则直接通过操作系统进行内存分配。内存池机制是 Python 内存管理的一部分,与引用计数和垃圾回收机制共同工作,以有效管理内存资源。
  • 垃圾回收则主要依赖引用计数机制,辅以标记-清除算法和分代回收策略,以解决循环引用问题并提高回收效率。引用计数记录每个对象被引用的次数,当引用计数降为零时,该对象将被视为垃圾并进行回收。标记-清除算法用于处理循环引用问题,而分代回收则通过将对象分为不同的代来提高回收效率。这些机制共同确保 Python 程序的高效执行,同时减轻了开发者在内存管理方面的负担。

is== 在比较对象时的内容是不同的。具体来说,is 比较的是两个对象的内存地址,以确定它们是否为同一个实例对象;而 == 则比较的是对象的值是否相等,这通常涉及到调用对象的 __eq__() 方法。

Python 中的整数缓存特性。对于小整数,范围在(-5 ~ 256)之间的整数,使用 ==is 运算符得到的结果是相同的。这是因为 Python 在内部建立了一个数组缓存,当创建小整数对象时,会直接引用缓存中已有的对象,而不是每次都创建新的对象。在 Python 脚本中运行代码时,编译器可以看到整个程序并进行优化,所以超出范围的整数也会直接引用缓存中已有的对象。不同的 Python 版本和代码运行环境可能会影响整数缓存的功能哦!


二、深拷贝与浅拷贝

深拷贝和浅拷贝是 Python 中两种重要的对象复制方法

浅拷贝是通过复制对象的引用而非对象本身来实现的。在顶层,原始对象和复制的对象是独立的对象(内存地址不同,可用 id() 函数查看),但里面的子对象都是引用。在 Python 中,我们可以利用 copy 模块的 copy() 函数来创建一个对象的浅拷贝。对于基本数据类型(整数、浮点数、布尔值、字符串)或只包含不可变对象的复合数据类型(列表、元组、字典、集合),浅拷贝是安全且高效的。但如果原始对象包含其他可变对象有嵌套的复杂对象,例如:列表中的列表和字典中的字典),则复制的对象将与原始对象共享内部子对象。这意味着对复制对象的内部子对象的修改也会反映在原始对象上。这就是为什么当原始对象包含其他可变子对象时,浅拷贝可能会带来问题

这里注意一点:直接赋值其实就是对象的引用(别名),都指向同一个对象。因此,直接赋值跟浅拷贝 copy.copy() 还是有一定区别的。

深拷贝用于确保原始对象与复制对象之间的完全独立。它递归地复制原始对象及其所有子对象,从而创建一个与原始对象完全独立的新对象。这意味着对深拷贝对象的修改完全不会影响原始对象。Python 的 copy 模块提供了 deepcopy() 函数,用于执行深拷贝。deepcopy() 的工作原理如下

  1. 检查对象的类型:首先,deepcopy() 会检查对象的类型。对于不同的类型,复制过程可能有所不同。
  2. 递归复制:对于嵌套的对象(如列表中的列表、字典中的字典和自定义对象等),deepcopy() 会复制原始对象及其所有子对象。这意味着它会继续对每个子对象执行深拷贝,直到遇到基本数据类型(如整数、字符串、浮点数等)为止。
  3. 处理循环引用:在复制过程中,deepcopy() 需要处理循环引用的情况。如果对象之间存在循环引用,deepcopy() 会跟踪这些引用,并确保在复制过程中不会创建无限递归的复制。
  4. 返回新对象:完成所有的复制后,deepcopy() 返回一个新的、与原始对象完全独立的复制对象。

浅拷贝适用于对象结构较为简单或仅需复制对象顶层结构的情况。而深拷贝则适用于对象结构复杂且需要完全独立副本的场景。在选择使用深拷贝还是浅拷贝时,应综合考虑对象的结构和复制需求。

虽然深拷贝提供了对象的完全独立性,但对于特别大的对象或包含复杂引用的对象,它比浅拷贝更耗时和消耗内存,因为它需要递归地复制原始对象及其所有子对象。此外,在某些情况下,如包含互相引用的对象,深拷贝可能会引起无限递归地尝试复制,直到达到 Python 的最大递归深度限制,从而引发 RecursionError。因此,在决定是否使用深拷贝时,需要根据实际需求权衡其优点和缺点。

总结:Python 中的深拷贝和浅拷贝对于有效地管理对象的复制至关重要。浅拷贝在对象结构较为简单、资源消耗较少的情况下提供了高效的复制方法,而深拷贝则适用于需要完全独立对象副本的复杂对象结构。在实际应用中,选择正确的拷贝方法可以避免潜在的程序错误并提高代码的效率。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、深拷贝与浅拷贝
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档