python中的浅拷贝和深拷贝

90 篇文章 2 订阅

1. 可变和不可变的数据类型

首先,我们知道Python3中,有6个标准的数据类型,他们又分为可以变不可变
不可变:Number(数字)、String(字符串)、Tuple(元组)。
可以变:List(列表)、Dictionary(字典)、Set(集合)。

2. 浅拷贝

2.1 浅拷贝的实现

1. li.copy()
2. li[:]
3. copy.copy()

2.2 浅拷贝的总结

1、对于不可变类型 Number ,String ,Tuple,浅拷贝仅仅是地址指向,不会开辟新空间。
2、对于可变类型 List,Dictionary,Set,浅拷贝会开辟新的空间地址(仅仅是最外层开辟了新的空间,里层的元素地址还是一样的),进行浅拷贝。
3、浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象的;改变原始对象中为不可变类型的元素的值,只有原始类型受影响。(操作拷贝对象对原始对象的也是同理)

2.3 可变类型和不可变类型在浅拷贝中的举例

对于不可变类型Number ,String ,Tuple,浅拷贝仅仅是地址指向,不会开辟新空间。

import copy
# 不可变类型 Number String Tuple

num1 = 17
num2 = copy.copy(num1)
print("num1:" + str(id(num1)))
print("num2:" + str(id(num1)))
# num1和num2的地址都相同


str1 = "hello"
str2 = copy.copy(str1)
print("str1:" + str(id(str1)))
print("str2:" + str(id(str2)))
# str1和str2的地址都相同

tup1 = (18, "tom")
tup2 = copy.copy(tup1)
print("tup1:" + str(id(tup1)))
print("tup2:" + str(id(tup2)))
# tup1和tup2的地址都相同

输出结果:

num1:9720992
num2:9720992
str1:140066872516784
str2:140066872516784
tup1:140066873313472
tup2:140066873313472

对于可变类型List,Dictionary,Set,浅拷贝会开辟新的空间地址(仅仅是最外层开辟了新的空间,里层的元素地址还是一样的),进行浅拷贝。

import copy
# 可变类型List Dictionary Set

list1 = [11,12]
list2 = copy.copy(list1)
print("list1:" + str(id(list1)))
print("list2:" + str(id(list2)))
# list1和list2的地址不相同


dic1 = [11,12,"hi"]
dic2 = copy.copy(dic1)
print("dic1:" + str(id(dic1)))
print("dic2:" + str(id(dic2)))
# dic1和dic2的地址不相同

set1 = {"AA","BB"}
set2 = copy.copy(set1)
print("set1:" + str(id(set1)))
print("set2:" + str(id(set2)))
# set1和set2的地址不相同

输出结果:

list1:139696778429632
list2:139696778480384
dic1:139696778579840
dic2:139696778579200
set1:139696778378592
set2:139696778378816

2.4 浅拷贝后对可变类型和不可变类型修改后的影响

import copy

l1 = [11, 12]
l2 = [21, 22]
num = 555

allOne = [l1, l2,num]

# 浅拷贝,创建出一个对象,并把旧对象元素的 引用地址 拷贝到新对象当中。
# 也就是说,两个对象里面的元素通过浅拷贝指向的还是同一个地址
allOne2 = copy.copy(allOne)

l1[0] = 16 # 此处修改,会使得 allOne 和 allOne2的第0个元素的值都发生改变,因为l1是List,是可变对象
allOne[2] = 666 # 此处修改,只会allOne的num的值,因为不可变对象一旦重新复制,地址就会发生改变。

num = 777 # 此处不会改变 allOne 和 allOne2的值,因为相当于 777 复制给一个全新的地址,这个num跟其他num已经没关系了

print(allOne)
print(allOne2)

print("id allOne:"+str(id(allOne)))
print("id allOne[0]:"+str(id(allOne[0])))
print("id allOne[1]:"+str(id(allOne[1])))
print("id allOne[2]:"+str(id(allOne[2])))

print("===========")
print("id allOne2:"+str(id(allOne2)))
print("id allOne2[0]:"+str(id(allOne2[0])))
print("id allOne2[1]:"+str(id(allOne2[1])))
print("id allOne2[2]:"+str(id(allOne2[2])))

输出结果:

[[16, 12], [21, 22], 666]
[[16, 12], [21, 22], 555]
id allOne:140030199287488
id allOne[0]:140030199124992
id allOne[1]:140030199212608
id allOne[2]:140030199556624
===========
id allOne2:140030199286848
id allOne2[0]:140030199124992
id allOne2[1]:140030199212608
id allOne2[2]:140030199556592

可以看出:
改动allOne中的可变类型,会影响allOne2,改变allOne2同理影响allOne。
改动allOne中的不可变类型,只有allOne自身会改变,allOne2不受影响。

对于不可变类型被修改后造成的影响,我们用一个更加简单的例子便可更好理解:

num = 123
print(str(id(num)))

num = 666
print(str(id(num)))

输出结果:

9724384
140147385174416

几乎可以说,Python 没有"变量",我们平时所说的变量其实只是"标签",是引用。

3. 深拷贝

3.1 深拷贝的实现

copy.deepcopy()

3.2 深拷贝的总结

1、浅拷贝,除了外层拷贝,还对子元素也进行了拷贝(本质上递归浅拷贝)。
2、经过深拷贝后,原始对象拷贝对象所有的元素地址没有相同的了。

3.3 深拷贝对不可变类型的影响

对于不可 变类型 Number String Tuple,深拷贝依然是地址指向,不会开辟新空间拷贝值

import copy
# 不可变类型 Number String Tuple

num1 = 17
num2 = copy.deepcopy(num1) # 深拷贝
print("num1:" + str(id(num1)))
print("num2:" + str(id(num1)))
# num1和num2的地址都相同


str1 = "hello"
str2 = copy.deepcopy(str1)  # 深拷贝
print("str1:" + str(id(str1)))
print("str2:" + str(id(str2)))
# str1和str2的地址都相同

tup1 = (18, "tom")
tup2 = copy.deepcopy(tup1) # 深拷贝
print("tup1:" + str(id(tup1)))
print("tup2:" + str(id(tup2)))
# tup1和tup2的地址都相同

输出结果:

num1:9720992
num2:9720992
str1:140259201016176
str2:140259201016176
tup1:140259201812672
tup2:140259201812672

3.4 深拷贝对可变类型的影响

对于可变类型 List、Dictionary、Set,深拷贝会开辟新的空间地址,进行拷贝。

import copy
#可变类型 List Dictionary Set

list1 = [11,12]
list2 = copy.deepcopy(list1) # 深拷贝
print("list1:" + str(id(list1)))
print("list2:" + str(id(list2)))
# list1和list2的地址不相同


dic1 = [11,12,"hi"]
dic2 = copy.deepcopy(dic1) # 深拷贝
print("dic1:" + str(id(dic1)))
print("dic2:" + str(id(dic2)))
# dic1和dic2的地址不相同

set1 = {"AA","BB"}
set2 = copy.deepcopy(set1) # 深拷贝
print("set1:" + str(id(set1)))
print("set2:" + str(id(set2)))
# set1和set2的地址不相同

输出结果:

list1:140017880180096
list2:140017880226816
dic1:140017880329664
dic2:140017880330176
set1:140017880133184
set2:140017880133408

从上面两个深拷贝的例子来看,我们对3种可变类型3种不可变类型进行深拷贝。结果发现,和浅拷贝几乎一致。
其实这也好理解,因为的深拷贝对比浅拷贝,强调的是递归,强调的是资源素。
对于外层的操作,深浅拷贝无异。

3.5 深拷贝会对内层子元素也进行拷贝

import copy

l1 = [11, 12]
l2 = [21, 22]
num = 555

allOne = [l1, l2,num]

# 深拷贝,除了外层拷贝,还对内层子元素也进行了拷贝(本质上递归浅拷贝)
# 经过深拷贝后,原始对象和拷贝对象所有的元素地址都没有相同的了

allOne2 = copy.deepcopy(allOne) # copy.deepcopy 深拷贝

allOne[1] = [113,114]
allOne2[2] = [227,228]

print(allOne)
print(allOne2)

print("id allOne:"+str(id(allOne)))
print("id allOne[0]:"+str(id(allOne[0])))
print("id allOne[1]:"+str(id(allOne[1])))
print("id allOne[2]:"+str(id(allOne[2])))

print("=========")
print("id allOne2:"+str(id(allOne2)))
print("id allOne2[0]:"+str(id(allOne2[0])))
print("id allOne2[1]:"+str(id(allOne2[1])))
print("id allOne2[2]:"+str(id(allOne2[2])))

输出结果:

[[11, 12], [113, 114], 555]
[[11, 12], [21, 22], [227, 228]]
id allOne:140221019303808
id allOne[0]:140221019145408
id allOne[1]:140221019302912
id allOne[2]:140221019572752
=========
id allOne2:140221019303168
id allOne2[0]:140221019303872
id allOne2[1]:140221019303552
id allOne2[2]:140221019303616

本例是跟浅拷贝做对比的。
在之前的浅拷贝中,子元素是不会开辟新空间做拷贝的。
而在深拷贝中,子元素也进行了拷贝。

4. 赋值,深拷贝和浅拷贝的区别

面试常问:请简述赋值,深拷贝和浅拷贝的区别?(python中如何拷贝一个对象?)

  • 直接赋值(li1 = li): 只传递对象的引用, li1指向对象li的内存地址空间,因此,原有列表li改变, 被赋值的li1也会做相应的改变.
  • 浅拷贝:li和li2的内存地址不同,但是子对象的内存地址相同, 因此,原始数据改变 ,子对象也改变。
  • 深拷贝(import copy, eg: li3=copy.deepcopy(li)), li和li3的内存地址不同,包含子对象的拷贝, 所以原始对象改变并不会造成深拷贝里面任何子项的改

当深拷贝和浅拷贝, 针对的对象全部是不可变数据类型时, 两者效果相同;
当深拷贝和浅拷贝, 针对的对象包含是可变数据类型时, 两者才有上述区别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值