Python深浅拷贝

Python深浅拷贝

1、深拷贝和浅拷贝概念理解

  • 浅拷贝,指的是重新分配一块内存创建一个新的对象,但里面的元素是原对象中各个子对象的引用
  • 深拷贝,是指重新配分一块内存,创建一个新的对象,并且将原对象种的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联

2、浅拷贝

  • 使用数据类型本身的构造器
  • 对于可变的序列,还可以通过切片操作符:来完成浅拷贝
  • Python还提供了对应的函数 copy.copy()函数,适用于任何数据类型

2.1 使用数据类型本身的构造器

list1 = [1, 2, 3]
list2 = list(list1)
print(list2)
print("list1==list2", list1 == list2)
print("list1 is list2", list1 is list2)
set1 = set([1, 2, 3])
set2 = set(set1)
print(set2)
print('set1==set2', set1 == set2)
print('set1 is set2', set1 is set2)
dict1 = {1: [1, 'w'], 2: 0, 3: 99}
dict2 = dict(dict1)
print(dict2)
print('dict1==dict2', dict1 == dict2)
print('dict1 is dict2', dict1 is dict2)
[1, 2, 3]
list1==list2 # True
list1 is list2 # False
{1, 2, 3}
set1==set2 # True
set1 is set2 # False
{1: [1, 'w'], 2: 0, 3: 99}
dict1==dict2 # True
dict1 is dict2 # False

分析: 浅拷贝,为新变量重新分配一块内存,和原来变量的内存不一样,所以有

list1 is list2 # False
set1 is set2 # False
dict1 is dict2 # False

但是浅拷贝完 两个变量种的元素值是一样的

list1==list2 # True
set1==set2 # True
dict1==dict2 # True

2.2 对于列表

对于列表,还可以通过切片符":" 来完成浅拷贝

list1 = [1, 2, 3]
list2 = list1[:]
print(list2)
print("list1==list2", list1 == list2)
print("list1 is list2", list1 is list2)

补充: 切片操作

Python中符合序列的有序序列都支持切片(slice),例如列表,字符串,元组。

存储对象[start : end : step]

参数

start : 起始索引,从0开始,-1表示结束
end:结束索引,不包含
step:步长;步长为正时,从左向右取值。步长为负时,反向取值

2.2.1 列表切片的使用

1. 根据位置信息提取列表中的元素
l1 = [5, 17, 135, 14, 9, 8, 1, 15]
ss = int(len(l1)/2)
print(l1[ss:]) # [9, 8, 1, 15]
print(l1[:ss]) # [5, 17, 135, 14]
# 最后一个
print(l1[-1]) # 15
# 最后3个
print(l1[-3:]) # [8, 1, 15]
# 取3-5数
print(l1[2:5]) # [135, 14, 9]
# 取1-10中 奇数 13579
print(l1[::2]) # [5, 135, 9, 1]
# 取1-10中 偶数
print(l1[1::2]) # [17, 14, 8, 15]
'''切片健壮性的提现'''
# 使用切片操作就不会产生该问题,会自动阶段或者返回空列表
print([l1[0:20:3]]) # [[5, 14, 1]] step=3
# 不会产生下标越界问题
print(l1[10:]) # []
2. 使用切片逆序列表- 反转列表 (Reverse a List)
print(l1[::-1]) # [15, 1, 8, 9, 14, 135, 17, 5]
3 修改多个列表元素值-可以使用切片赋值一次修改多个列表元素
l1 = [5, 17, 135, 14, 9, 8, 1, 15]
l1[1:3] = ['pop','up']
print(l1) # [5, 'pop', 'up', 14, 9, 8, 1, 15]
4 插入多个列表元素-在列表中插入项目,而无需替换任何内容
l1 = [5, 17, 135, 14, 9, 8, 1, 15]
l1 = [:0]=['a','b'] # 在头部插入
# 通过指定切片的开始索引和停止索引将元素插入到列表中的中间
mid=int(len(l1)/2)
l1[mid:mid]=['ok','no']
print(l1)
5 删除多个列表元素
l1[5:]=[]
6 克隆或复制列表
l2 = l1[:]

2.3 使用 copy.copy() 函数

函数 copy.copy(),适用于任何数据类型

import copy
list1 = [1, 2, 3]
list2 = copy.copy(list1)
print(list2)
print("list1==list2", list1 == list2)
print("list1 is list2", list1 is list2)
set1 = set([1, 2, 3])
set2= copy.copy(set1)
print(set2)
print('set1==set2', set1 == set2)
print('set1 is set2', set1 is set2)
dict1 = {1: [1, 'w'], 2: 0, 3: 99}
dict2 = copy.copy(dict1)
print(dict2)
print('dict1==dict2', dict1 == dict2)
print('dict1 is dict2', dict1 is dict2)
[1, 2, 3]
list1==list2 True
list1 is list2 False
{1, 2, 3}
set1==set2 True
set1 is set2 False
{1: [1, 'w'], 2: 0, 3: 99}
dict1==dict2 True
dict1 is dict2 False

2.4 对于元组

对于元组,使用tuple()或者切片操作符 ':' 不会创建一份浅拷贝,相反他会返回一个指向相同元组的引用:

tuple1 = (1, 2, 3)
tuple2 = tuple(tuple1)
print(tuple2)
print('tuple1==tuple2', tuple1 == tuple2)
print('tuple1 is tuple2', tuple1 is tuple2)
tuple1 = (1, 2, 3)
tuple2 = tuple1[:]
print(tuple2)
print('tuple1==tuple2', tuple1 == tuple2)
print('tuple1 is tuple2', tuple1 is tuple2)
(1, 2, 3)
tuple1==tuple2 True
tuple1 is tuple2 True
(1, 2, 3)
tuple1==tuple2 True
tuple1 is tuple2 True

使用 tuple() 或者切片操作符":",不会创建一份浅拷贝,因为它开辟新的内存存储的是原对象的引用,而没有创建新的对象来存储原对象的子对象的引用,所以不是浅拷贝。相反它会返回一个指向相同元组的引用

对字符串使用str()或者切片操作符':',原理和元组 相同

str1 = 'operation'
str2 = str1[:]
print(str2)
print('str1==str2', str1 == str2)
print('str1 is str2', str1 is str2)
operation
str1==str2 True
str1 is str2 True
str1 = 'operation'
str2 = str(str1)
print(str2)
print('str1==str2', str1 == str2)
print('str1 is str2', str1 is str2)
operation
str1==str2 True
str1 is str2 True

2.5 关于切片操作符 ':'

切片操作符':'不能用于字典和集合完成浅拷贝

# set1 = {1, 2, 3}
# set2 = set1[:]
# print(set2) # 报错
# dict1 = {1:1,2:2,3:3}
# dict2 = dict1[:]

2.6 和赋值的区别

和赋值的本质区别在于,赋值只是把元对象的引用给到新对象

set1 = {1: 1, 2: 2, 3: 3}
set2 = set1
print(set2)
print('set1==set2', set1 == set2)
print('set1 is set2', set1 is set2)
print(id(set1))
print(id(set2))
{1: 1, 2: 2, 3: 3}
set1==set2 True
set1 is set2 True
2523539046272
2523539046272

2.7 浅拷贝需要注意的问题

对数据采用先拷贝的方式时,如果原对象中的元素不可变,那倒无所谓;但如果元素可变 ,浅拷贝通常会出现一些问题

list1 = [[1, 2], (30, 40)]
list2 = list(list1)
list1.append(100)
print("list1:", list1)
print("list2:", list2)
list1[0].append(3)
print("list1:", list1)
print("list2:", list2)
list1[1] += (50, 60)
print("list1:", list1)
print("list2:", list2)

result:

list1: [[1, 2], (30, 40), 100]
list2: [[1, 2], (30, 40)]
list1: [[1, 2, 3], (30, 40), 100]
list2: [[1, 2, 3], (30, 40)]
list1: [[1, 2, 3], (30, 40, 50, 60), 100]
list2: [[1, 2, 3], (30, 40)]

3 深拷贝

Python 中以 copy.deepcopey()来实现对象的深度拷贝

list1 = [[1, 2], (30, 40)]
list2 = copy.deepcopy(list1)
list1.append(100)
print("list1:", list1)
print("list2:", list2)
list1[0].append(3)
print("list1:", list1)
print("list2:", list2)
list1[1] += (50, 60)
print("list1:", list1)
print("list2:", list2)

4 结论

深拷贝出来的对象 就是完完全全的新对象,不管是对象本身(id),还是对象中包含的子对象,都和原始对象不一样;

浅拷贝出来的对象就是外新内旧的对象,对象本身(id)和原始对象完全不同,但是子对象和原始对象的子对象是一样的。

再补充说下赋值,赋值来的对象就是完全的原始对象,只是叫的名字不同了

5 应用场景

  1. 我们处理中间结果往往不想对原始数据进行修改,所以这个时候可以使用深拷贝
  2. 如果我们只想新增一个辅助列(只涉及对父类对象的修改),那这时我们可以使用浅拷贝,节约系统内存。

posted @   性格如此w  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示