Python的变量存储机制:浅拷贝与深拷贝详解

简介: 不可变数据类型在内存中存储的值仅存储一份,后续定义的变量如果值相等都指向用一个对象,为什么是这样呢?

这是机器未来的第8篇文章



1. 变量的存储机制

Python中的一切都是对象,变量是对象的引用!对象存于堆中,变量存于栈中。

1.1 什么是堆、栈?

堆栈都存在与内存中,在运行时分配的内存空间。对象存于堆中,变量存于栈中, 堆区存变量值, 栈区存变量名。栈区存放变量名和其变量值的内存地址, 通过这个内存地址, 变量名可以找到变量值。

1.2 直接引用和间接引用

1.2.1 直接引用:变量名直接关联变量值

直接引用常见于整数类型和字符串类型,修改它们的值,其实已经指向了其它的对象。直接引用的数据类型也被称为不可变数据类型, *不可变数据类型在内存中存储的值仅存储一份,后续定义的变量如果值相等都指向用一个对象,即 x1 is x2 and x1 == x2为True*.

变量名(变量值的地址)存于内存栈区,变量值存于堆区, 变量名直接关联变量值。

x = 10

y = 20

print(hex(id(x)), hex(id(y)))

0x7ff97c90f020 0x7ff97c90f160    # 从输出中可知,x,y指向两个不一样的对象

当执行x = y 时,你会发现x已经指向了一个新的对象,和原来的对象链路已经断开了。

x = 10

y = 20

print(hex(id(x)), hex(id(y)))

x = y

print(hex(id(x)), hex(id(y)))

0x7ff97c90f020 0x7ff97c90f1600x7ff97c90f160 0x7ff97c90f160    # 从输出中可知,x,y已经指向了同一个对象

注意:字符串的内容是不可以更改的,修改会直接报错!!!

name = "David"

name[2] = 'a'

TypeError                 Traceback (most recent call last)C:\Users\ZHOUSH~1\AppData\Local\Temp/ipykernel_9084/4053774855.py in <module>1 name = "David"

2 name[2] = 'a'

TypeError: 'str' object does not support item assignment

1.2.2 间接引用:变量名通过列表对象间接访问变量值

间接引用出现在容器类型里,如列表、元组、字典等。

定义列表变量l,其存储结构如图:变量l存储于栈区,变量l通过存储在堆区中列表对象内存地址访问列表对象,然后列表对象再通过其列表中存储的元素地址访问具体的变量值'a' 和 'b'。

l = ['a', 'b']

如果修改变量l[1]的值,例如l[1] = 'c',则列表的存储地址不会发生变化,但是列表对象第0个元素的地址会发生变化,其为字符'c'的内存地址,并指向字符对象'c',但是整个过程变量l的地址不会发生变化.

特别注意:间接引用变量既是值相等,这两个变量也不一定是同一个变量;而对于不可变数据类型,只要值相等,那么这两个变量一定是同一个变量.

x1 = [1, 2, 3, 4, 5, 3, 6, 7]

x2 = [1, 2, 3, 4, 5, 3, 6, 7]

x1 is x2

print(hex(id(x1)), hex(id(x2)))

0x2913b25ab08 0x2913cdf35c8

2. 浅拷贝与深拷贝

  • 直接赋值:其实就是对象的引用(别名)。
  • 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。
  • 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。

2.1 直接赋值

其实就是对象的引用(别名)

l1 = [1, 'abc', [2, 3]]

l2 = l1

print(id(l1), id(l2))

2822815412936 2822815412936

l2直接指向l1引用的对象,l1和l2的内存地址是一样的, 存储结构如图.

2.2 浅拷贝

拷贝父对象,不会拷贝对象的内部的子对象。

import copy

l1 = [1, 'abc', [2, 3]]

l2 = copy.copy(l1)

print(id(l1), id(l2))

print('l1:', id(l1[0]), id(l1[1]), id(l1[2]))

print('l2:', id(l2[0]), id(l2[1]), id(l2[2]))

2822786373512 2822786371656       # l1和l2的内存地址不一样,是两个不一样的变量l1: 140709513457408 2822710032120 2822785142088 l2: 140709513457408 2822710032120 2822785142088 # l1和l2的元素的内存地址是一样的, 包括元素中的列表变量(是直接指向过去的), 验证了仅拷贝父对象的描述.

2.3 深拷贝

copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。

import copy

l1 = [1, 'abc', [2, 3]]

l2 = copy.deepcopy(l1)

print(id(l1), id(l2))

print('l1:', id(l1[0]), id(l1[1]), id(l1[2]), id(l1[2][0]), id(l1[2][1]))

print('l2:', id(l2[0]), id(l2[1]), id(l2[2]), id(l2[2][0]), id(l2[2][1]))

2822786372360 2822815414472   # l1和l2的内存地址不一样,是不同的变量l1: 140709513457408 2822710032120 2822786363528 140709513457440 140709513457472l2: 140709513457408 2822710032120 2822786366024 140709513457440 140709513457472

l2对l1进行了深拷贝,直至数据类型为不可变类型为止.

从输出中可以看到:

  • l1和l2的内存地址不一样了,是不同的变量;
  • l1和l2的前2个成员变量的内存是一样的, 因为它们直接引用的不可变数据类型;
  • 第3个成员变量为一个列表,深拷贝时创建了一个新的列表变量,从输出可知l1[2]的内存地址为2822786363528, l2[2]的内存地址为2822786366024.
  • 但l1[2]和l[3]列表中的元素的内存地址又变为一样的了,因为它们都是不可变数据类型,指向同一个对象(注:不可变数据类型仅在内存中存储一份).
相关文章
|
1月前
|
存储 算法 数据挖掘
【2023年中国高校大数据挑战赛 】赛题 B DNA 存储中的序列聚类与比对 Python实现
本文介绍了2023年中国高校大数据挑战赛赛题B的Python实现方法,该赛题涉及DNA存储技术中的序列聚类与比对问题,包括错误率分析、序列聚类、拷贝数分布图的绘制以及比对模型的开发。
39 1
【2023年中国高校大数据挑战赛 】赛题 B DNA 存储中的序列聚类与比对 Python实现
|
14天前
|
Python
Python变量的作用域_参数类型_传递过程内存分析
理解Python中的变量作用域、参数类型和参数传递过程,对于编写高效和健壮的代码至关重要。正确的应用这些概念,有助于避免程序中的错误和内存泄漏。通过实践和经验积累,可以更好地理解Python的内存模型,并编写出更优质的代码。
10 2
|
2天前
|
消息中间件 安全 数据库
动手实操!Python IPC机制,打造高效协同的进程军团
【9月更文挑战第10天】在软件开发领域,进程间的高效协作对应用性能与稳定性至关重要。Python提供了多种进程间通信(IPC)机制,如管道、消息队列、套接字、共享内存等,帮助开发者构建高效协同的系统。本文将通过动手实践,使用`multiprocessing`模块演示如何利用队列实现进程间通信。示例代码展示了如何创建一个工作进程从队列接收并处理数据,从而实现安全高效的进程交互。通过实际操作,读者可以深入了解Python IPC的强大功能,提升系统的并发处理能力。
9 0
|
28天前
|
存储 数据采集 人工智能
一:《Python基础语法汇总》— 变量与数据类型
【8月更文挑战第15天】本篇文章详细讲述了关于变量的命名规范,id()函数及Python的数据类型与强制转换
16 2
|
30天前
|
开发者 Python
Python中的异常处理机制及其实践
【8月更文挑战第12天】Python的异常处理机制通过`try`和`except`结构显著提高了程序的稳定性和可靠性。在`try`块中执行可能引发异常的代码,如果发生异常,控制权将转移到与该异常类型匹配的`except`块。此外,还可以通过`else`处理无异常的情况,以及使用`finally`确保某些代码无论如何都会被执行,非常适合进行清理工作。这种机制允许开发者精确地捕捉和管理异常,从而提升程序的健壮性和可维护性。同时,Python还支持定义自定义异常,进一步增强了错误处理的灵活性。
38 4
|
1月前
|
监控 测试技术 数据库
Python自动化测试之异常处理机制
总体而言,妥善设计的异常处理策略让自动化测试更加稳定和可靠,同时也使得测试结果更加清晰、易于理解和维护。在设计自动化测试脚本时,务必考虑到异常处理机制的实现,以保证测试过程中遇到意外情况时的鲁棒性和信息的有效传达。
35 2
|
1月前
|
存储 Python 容器
python声明变量
【8月更文挑战第4天】
39 8
|
11天前
|
数据采集 存储 JavaScript
构建你的首个Python网络爬虫:抓取、解析与存储数据
【8月更文挑战第31天】在数字时代的浪潮中,数据成为了新的石油。了解如何从互联网的海洋中提取有价值的信息,是每个技术爱好者的必备技能。本文将引导你通过Python编程语言,利用其强大的库支持,一步步构建出你自己的网络爬虫。我们将探索网页请求、内容解析和数据存储等关键环节,并附上代码示例,让你轻松入门网络数据采集的世界。
|
1月前
|
数据处理 Python
python变量重新赋值
【8月更文挑战第4天】
38 6
|
1月前
|
存储 IDE 开发工具
Python中变量命名规则
【8月更文挑战第5天】
29 4