首页 > 编程语言 >Python:深拷贝与浅拷贝

Python:深拷贝与浅拷贝

时间:2023-10-23 16:58:03浏览次数:33  
标签:Python 数据类型 对象 print 拷贝 copy id

python:深拷贝与浅拷贝

一、了解几个概念

  • 变量:是一个系统表的元素,拥有指向对象的连接空间

  • 对象:被分配的一块内存,存储所代表的值

  • 引用:是自动形成的从变量到对象的指针

  • 类型:属于对象,而非变量

  • 不可变对象:一旦创建就不可修改的对象(值内存地址固定后不可以再修改其值),包括字符串、元组、数值类型(整型、浮点型)、布尔类型

该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。

  • 可变对象:可以修改的对象(值内存地址固定后还可以修改其值),包括列表、字典、集合

该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,通俗点说就是原地改变。

当我们写:

a = 'python'

① 创建变量 a (栈)

② 创建一个对象(分配一块内存空间),来存储值“python” (数据区)

③ 将变量与对象,通过指针连接起来,从变量到对象的连接称之为引用(变量引用对象)

image-20231019215615031

赋值:只是复制了新对象的引用,不会开辟新的内存空间(将原有数据打上新的标签)

image-20231019220233726

二、浅拷贝

拷贝就是复制操作,其拷贝的值是一样的,重点观察引用关系的变化

浅拷贝:创建新对象,其内容是原对象的引用

浅拷贝之所以称为浅拷贝,是因为它仅仅只拷贝一层,拷贝了最外围的对象本身,内部的元素都只是拷贝一个引用而已。

基本语法:

import copy
copy.copy(要拷贝的变量名称)

可变数据类型的浅拷贝:

import copy

# 对于可变数据类型的浅拷贝
list1 = [1, 3, 5]
list2 = copy.copy(list1)
print(list1, id(list1))  # [1, 3, 5] 2029785917952
print(list2, id(list2))  # [1, 3, 5] 2029788974656
  • 对于简单的可变数据类型,浅拷贝相当于将原对象的值进行拷贝,需要在内存空间中开辟一块新的内存空间

image-20231019221633149

import copy

list1 = [1, 3, 5, [7, 9]]
list2 = copy.copy(list1)
print(list1, id(list1))  # [1, 3, 5, [7, 9]] 1757227908928
print(list2, id(list2))  # [1, 3, 5, [7, 9]] 1757227822720

# 但是对于内层[7.9],这也是一个列表,也需要占用内存地址,那么其拷贝过程的内存是否相同呢
print(id(list1[3]))  # 2710364155712
print(id(list2[3]))  # 2710364155712
  • 对于复杂的可变数据类型,浅拷贝只能拷贝可变数据类型的最外层对象,而无法拷贝内层对象,所以只需要为最外层对象开辟内存空间,内层对象拷贝之后的引用关系和原对象保持不变

image-20231019222741554

  • 结论:浅拷贝能力有限,只能拷贝最外层对象(只需要为最外层对象开辟内存空间),而无法拷贝内层对象

不可变数据类型的浅拷贝

import copy

tuple1 = (1, 3, 5)
tuple2 = copy.copy(tuple1)

print(tuple1, id(tuple1))  # (1, 3, 5) 1831160185728
print(tuple2, id(tuple2))  # (1, 3, 5) 1831160185728
  • 对于简单的不可数据类型,由于不可变数类型地址一旦固定,其值就无法改变了,又由于浅拷贝需要把自身的对象空间赋值给另外一个对象,为了保持数据一致,只能让其指向相同的内存空间(不需要额外开辟内存空间)

image-20231019224248298

  • 对复杂的不可变数据类型,浅拷贝也只能拷贝最外层对象,无法拷贝内层对象

浅拷贝的三种形式:

切片操作、工厂函数(数据类型转换)、copy模块中的copy函数

切片操作:list1 = list[ : ] 或 list1 = [i for i in list ]

工厂函数:list1 = list(list2)

copy函数:list1 = copy.copy(list)

浅拷贝案例:

a = [1, 3, 5, [7, 9]]
b = a[:]
a[1] = 2
a[3][1] = 10

print(b)
# 问b输出结果多少?
# [1, 3, 5, [7, 10]]

浅拷贝小结:

1)当浅复制的值是不可变对象(字符串、元组、数值类型)时和“赋值”的情况一样,对象的id值(id()函数用于获取对象的内存地址)与浅复制原来的值相同。

2)当浅复制的值是可变对象(列表、字典、集合)时会产生一个“不是那么独立的对象”存在。有两种情况:

第一种情况:复制的对象中无复杂子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。

第二种情况:复制的对象中有复杂子对象(例如列表中的一个子元素是一个列表),如果不改变其中复杂子对象,浅复制的值改变并不会影响原来的值。 但是改变原来的值中的复杂子对象的值会影响浅复制的值。

三、深拷贝

深拷贝:与浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关系。

所以改变原有被复制对象不会对已经复制出来的新对象产生影响。

深拷贝只有一种形式:使用copy模块中的deepcopy函数

import copy
data = copy.deepcopy(data)

简单的可变类型深拷贝:

import copy

a = [1, 3, 5]
b = copy.deepcopy(a)
print(a, id(a))  # [1, 3, 5] 2434688069312
print(b, id(b))  # [1, 3, 5] 2434690765888

复杂的可变类型深拷贝:

import copy

a = [1, 3, 5, [7, 9]]
b = copy.deepcopy(a)
print(a, id(a))  # [1, 3, 5, [7, 9]] 2162810741504
print(b, id(b))  # [1, 3, 5, [7, 9]] 2162810727424
print(id(a[3]))  # 2162810727040
print(id(b[3]))  # 2162810727232
  • 对于可变数据类型的深拷贝,深拷贝拷贝了所有的数据并开辟对应的内存空间储存。

不可变数据类型的深拷贝:

import copy

a = (1, 3, 5, (7, 9))
b = copy.deepcopy(a)
print(a, id(a))  # (1, 3, 5, (7, 9)) 2029346995712
print(b, id(b))  # (1, 3, 5, (7, 9)) 2029346995712

print(id(a[3]))  # 2029346562432
print(id(b[3]))  # 2029346562432
  • 对于不可变数据类型的深拷贝,不管是简单的还是复杂的,深拷贝都只能对象的引用关系,他们指向了相同的内存空间

四、深浅拷贝中的特殊情况

案例1:可变嵌套不可变数据类型

import copy

a = [1, 3, 5, (7, 9)]
b = copy.copy(a)
c = copy.deepcopy(a)
print(id(a))  # 1186799997824
print(id(b))  # 1186799988352
print(id(c))  # 1186799988672

print(id(a[3]))  # 1700778811776
print(id(b[3]))  # 1700778811776
print(id(c[3]))  # 1700778811776

  • 外层对象是可变数据类型,所以可以进行完全拷贝(需要生成内存空间),但是内层对象是不可变数据类型,所以只能进行拷贝引用关系

案例2:不可变嵌套可变数据类型

d = (1, 3, 5, [7, 9])
e = copy.copy(d)
f = copy.deepcopy(d)

print(id(d))  # 2193468143712
print(id(e))  # 2193468143712
print(id(f))  # 2193468262576 内存地址不一样了

print(id(d[3]))  # 2764873530496
print(id(e[3]))  # 2764873530496
print(id(f[3]))  # 2764873530560
  • 如果一个不可变数据类型包含了可变数据类型,浅拷贝的结论与之前一致,都只能拷贝引用关系。但是对于深拷贝而言,这种数据类型整体都可以进行完全拷贝

image-20231020165800762

标签:Python,数据类型,对象,print,拷贝,copy,id
From: https://www.cnblogs.com/luoluoange/p/17782820.html

相关文章

  • 栩栩如生,音色克隆,Bert-vits2文字转语音打造鬼畜视频实践(Python3.10)
    诸公可知目前最牛逼的TTS免费开源项目是哪一个?没错,是Bert-vits2,没有之一。它是在本来已经极其强大的Vits项目中融入了Bert大模型,基本上解决了VITS的语气韵律问题,在效果非常出色的情况下训练的成本开销普通人也完全可以接受。BERT的核心思想是通过在大规模文本语料上进行无监督预......
  • Java基础 文件拷贝的基本代码
    FileInputStreamfis=newFileInputStream("E:\\Java基础资料\\a.txt");FileOutputStreamfos=newFileOutputStream("E:\\Java基础资料\\b.txt");while(true){intb=fis.read();if(b==-1)break;fos.write(b);}fos.close......
  • python通过脚本路径获取对应脚本里的内容
    importinspectfromimportlib.utilimportspec_from_file_location,module_from_specscript_path="test.py"spec=spec_from_file_location("test",script_path)module=module_from_spec(spec)spec.loader.exec_module(module)print(modul......
  • python 计算指定日期是今年的第几周和这个月的第几周
    Python当前时间是一年中第几周_python计算一年的第几周-CSDN博客以上感觉可能索引是从0开始ISO8601每个日历星期从星期一开始,星期日为第7天。第一个日历星期有以下四种等效说法:1,本年度第一个星期四所在的星期;2,1月4日所在的星期;3,本年度第一个至少有4天在同一星期内的星......
  • 《安富莱嵌入式周报》第321期:开源12导联便携心电仪,PCB AI设计,150M示波器差分探头,谷歌
     视频版:https://www.bilibili.com/video/BV1ju4y1D7A8/1、开源12导联便携心电仪https://voltagedivide.com/2017/10/14/psoc-design-and-implementation-of-a-12-lead-portable-ecg/这个开源有完整的上位机,下位机和原理图,并且有一个详细的设计论文。12导联心电图是心电图检查中常......
  • Python工具箱系列(四十四)
    使用py7zr对目录与文件进行压缩打包 7z是一种主流高效的压缩格式,它拥有极高的压缩比。在计算机科学中,7z是一种可以使用多种压缩算法进行数据压缩的档案格式。该格式最初被7-Zip实现并采用,但是这种档案格式是公有的,并且7-Zip软件本身亦在GNU宽通用公共许可证(GNULGPL)协议下开......
  • ubuntu20.04下源码编译python 3.12
    需要注意的地方 1.安装依赖:https://devguide.python.org/getting-started/setup-building/#build-dependenciessudoapt-getinstallbuild-essentialgdblcovpkg-config\libbz2-devlibffi-devlibgdbm-devlibgdbm-compat-devliblzma-dev\libnc......
  • 图书推荐与管理系统Python+协同过滤推荐算法+Django网页界面
    一、介绍图书管理与推荐系统。使用Python作为主要开发语言。前端采用HTML、CSS、BootStrap等技术搭建界面结构,后端采用Django作为逻辑处理,通过Ajax等技术实现数据交互通信。在图书推荐方面使用经典的协同过滤算法作为推荐算法模块。主要功能有:角色分为普通用户和管理员普通用户可注......
  • [924] f-strings in Python
    ref:f-stringsinPythonref:Python'sF-StringforStringInterpolationandFormattingF-strings,alsoknownasformattedstringliterals,areafeatureintroducedinPython3.6thatprovideaconciseandconvenientwaytoembedexpressionsinside......
  • 分享一个批量转换某个目录下的所有ppt->pdf的Python代码
    大家好,我是皮皮。一、前言前几天在Python最强王者群【Python小小小白】分享了一份Python自动化办公的代码,可以批量转换某个目录下的所有ppt->pdf,非常强大。二、实现过程在正式跑代码之后,你可能需要按照对应的库,不然会报错。代码运行之后,本地会出现下面的UI界面,选择PPT文件夹即可,然......