python赋值、浅拷贝、深拷贝总结概述

背景

python中赋值、浅拷贝、深拷贝这三个知识点,表面看都是对一个对象复制为另一个对象,但由于涉及到对象值和对象地址,所以这三个知识点在细节方面,有所差异。所以做个总结。

概述

先罗列如下概念:
赋值:
python中的“=”符号,比如a = 1, b = a。即指定一个对象的引用。赋值不会复制对象。
浅拷贝:
构造一个新的复合对象,然后(在尽可能的范围内)将原始对象中找到的对象的 引用插入其中。
内置模块copy中的方法:copy.copy(a)
可变对象的copy方法,几乎都是浅拷贝。比如列表、集合、字典、队列等的copy方法。
深拷贝:
构造一个新的复合对象,然后,递归地将在原始对象里找到的对象的副本插入其中。
内置模块copy中的方法:copy.deepcopy(a)
深度复制操作通常存在两个问题, 而浅层复制操作并不存在这些问题:

  1. 递归对象 (直接或间接包含对自身引用的复合对象) 可能会导致递归循环。
  2. 由于深层复制会复制所有内容,因此可能会过多复制(例如本应该在副本之间共享的数据)。

不可变对象:
对象的值是不可变的。我们不可以修改该对象的值。该对象的值和地址一定是唯一对应的。如果强行赋予引用该对象的变量新的值,相当于创建了一个新对象,新的对象有新的值和新的地址。如果一个变量引用的是一个不可变对象a,然后赋予该变量新的值,那么此时创建了新的不可变对象b,该变量的引用也变为不可变对象b。原来的不可变对象a没有任何改变。没有任何改变的含义是:不可变对象a的对象值和对象地址都没有改变。
不可变对象数据类型:
int,float,string,tuple,boolean,bytes

示例:

# 查看不可变对象123的地址
print(id(123))

# 变量a引用不可变对象123
a = 123
# 查看变量a的值和地址
print(a,id(a))

# 变量a引用不可变对象123
a = 123
# 查看变量a的值和地址
print(a,id(a))

# 变量a引用不可变对象124
a = 124
# 查看变量a的值和地址
print(a,id(a))
# 查看不可变对象123的地址
print(id(123))

'''
stdout:
140725701763936
123 140725701763936
123 140725701763936
124 140725701763968
140725701763936
[Finished in 0.8s]
'''

可变对象:
对象的值是可变的,即基于对象当前地址,对象的值可以被改变。如果一个变量引用可变对象a,可变对象的值被改变后,该变量仍然引用可变对象a,变量的值随着可变对象a的值改变。
python中的可变对象:list,dict,set
示例:

# 查看可变对象list的地址
print(id([]))

# 变量a引用可变对象
a = []
# 查看变量a的值和地址
print(a,id(a))


# 修改变量a引用的list对象的值
a.append('22')
# 查看变量a的值和地址
print(a,id(a))

# 修改变量a引用的list对象的值
a.append('33')
# 查看变量a的值和地址
print(a,id(a))




'''
stdout:
1875402515016
[] 1875402515016
['22'] 1875402515016
['22', '33'] 1875402515016
[Finished in 0.8s]
'''


对于不可变对象,由于赋值、浅拷贝、深拷贝的作用效果是相同的。所以可以理解为,对于不可变对象,不用区分赋值、浅拷贝、深拷贝。不可变对象也用不上使用浅拷贝和深拷贝。
所以,当作用对象是可变对象时,我们才需要注意赋值、浅拷贝、深拷贝的使用场景。

概念:

赋值:
假设我们已经明白了引用的概念。
将对象的引用赋予某一个变量。赋值号的两边都是指向同一个对象。
赋值号的左边是右边对象的引用。
比如:a = 123。赋值号右边123是个不可变对象,将其引用赋予变量a,此时变量a是对象123的引用。
在面向对象中,引用也是个对象。所以:
b = a。赋值号右边是一个123的引用,此时将该引用的引用赋予b(有点绕),简化一下,可以理解为此时b也是123的引用。此时b和a都指向同一个值,即123。

浅拷贝:
可以直接根据字面意思理解,浅拷贝就是浅层次的拷贝。
什么是浅层次呢?
可以把一个可变对象序列当作父对象
一个可变对象序列,作为父对象,会包含很多子对象,包括不可变子对象和可变子对象。相信这一点,接触过序列都可以理解。
可以把父对象当作浅层次,浅拷贝只会拷贝父对象,不会拷贝子对象(只是把子对象的引用插入到新对象)。
拷贝父对象意味着,会在内存中新开辟一块内存来保存拷贝过来的父对象。
不会拷贝子对象意味着:拷贝出来的对象中子对象,依然是原来父对象中子对象的引用,并没有为子对象新开辟内存。对于不可变子对象,倒是没有影响;但是对于可变子对象,如果新对象或旧对象中的可变子对象中任一方改变,那么新旧对象中的子对象是同时发生这种改变的。
比如说一个人和他的一只猫作为一个可变对象序列,这只猫是是这个人的子对象。有一天,这个人猫组合通过浅拷贝的方式被拷贝到另一个宇宙中去了,此时两个宇宙中会分别存在这样一个人,但只有原来的宇宙会真实存在这只猫,后来的宇宙中只有这只猫的投影。如果原来的宇宙中那只猫生病了,那么投影猫也会是一副生病的样子。并且,投影猫生病了,原来的宇宙中那只猫,也会生病,因为他们就是同一只猫。但是原来宇宙中的人生病了,不会影响到第二个宇宙中的人,虽然他们看起来一摸一样,但确是两个不同的人了。
深拷贝:
深拷贝根据字面意思理解,就是深层次的拷贝。

既拷贝父对象,也拷贝子对象中的可变子对象。这里仅仅是可变子对象,新对象中不可变子对象的内存地址依然和旧对象中不可变子对象的内存地址相同。因为既然是不可变子对象,拷贝其引用不会造成任何影响。
在浅拷贝的基础上,除了对拷贝出来的父对象开辟一跨新内存之外,也会对子对象中的可变子对象开辟一块新内存。
此时可以理解新旧对象已经完全分离了,至少在使用上没有任何关系了。
依然拿上面的人和猫举例,
此时是通过深拷贝的方式拷贝到另一个宇宙中的,那么此时无论原来宇宙中的人或猫发生了什么,新宇宙中的人和猫都不会受到任何影响。反之依然。

总结
通过以上深拷贝和浅拷贝的定义,我们可以明白,如果可变对象序列中只有不可变子对象,那么深拷贝和浅拷贝的作用效果是相同的。

对不可变对象进行赋值,浅拷贝,深拷贝操作

示例1:

import copy
a = 1
b = a
c = copy.copy(a)
d = copy.deepcopy(a)

print(id(a))
print(id(b))
print(id(c))
print(id(d))

'''
140725686228000
140725686228000
140725686228000
140725686228000
[Finished in 0.8s]
'''

对可变对象进行赋值,浅拷贝,深拷贝操作

示例2:

import copy
a = ['11','22',('a','b'),[1,2]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)


print("源对象     :",a, id(a), id(a[2]),  id(a[3]))
print("赋值对象   :",b, id(b), id(b[2]),  id(b[3]))
print("浅拷贝对象 :",c, id(c), id(c[2]),  id(c[3]))
print("深拷贝对象 :",d, id(d), id(d[2]),  id(d[3]))

#修改源对象中父对象的值
a.append('33')
print('################')

print("源对象     :",a, id(a), id(a[2]),  id(a[3]))
print("赋值对象   :",b, id(b), id(b[2]),  id(b[3]))
print("浅拷贝对象 :",c, id(c), id(c[2]),  id(c[3]))
print("深拷贝对象 :",d, id(d), id(d[2]),  id(d[3]))

'''
源对象     : ['11', '22', ('a', 'b'), [1, 2]] 1895380173960 1895379383304 1895380175176
赋值对象   : ['11', '22', ('a', 'b'), [1, 2]] 1895380173960 1895379383304 1895380175176
浅拷贝对象 : ['11', '22', ('a', 'b'), [1, 2]] 1895381343176 1895379383304 1895380175176
深拷贝对象 : ['11', '22', ('a', 'b'), [1, 2]] 1895381343240 1895379383304 1895381342920
################
源对象     : ['11', '22', ('a', 'b'), [1, 2], '33'] 1895380173960 1895379383304 1895380175176
赋值对象   : ['11', '22', ('a', 'b'), [1, 2], '33'] 1895380173960 1895379383304 1895380175176
浅拷贝对象 : ['11', '22', ('a', 'b'), [1, 2]] 1895381343176 1895379383304 1895380175176
深拷贝对象 : ['11', '22', ('a', 'b'), [1, 2]] 1895381343240 1895379383304 1895381342920
[Finished in 0.8s]
'''

示例3:

import copy
a = ['11','22',('a','b'),[1,2]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)


print("源对象     :",a, id(a), id(a[2]),  id(a[3]))
print("赋值对象   :",b, id(b), id(b[2]),  id(b[3]))
print("浅拷贝对象 :",c, id(c), id(c[2]),  id(c[3]))
print("深拷贝对象 :",d, id(d), id(d[2]),  id(d[3]))

#修改源对象中可变对象的值
a[3].append(3)
print('################')

print("源对象     :",a, id(a), id(a[2]),  id(a[3]))
print("赋值对象   :",b, id(b), id(b[2]),  id(b[3]))
print("浅拷贝对象 :",c, id(c), id(c[2]),  id(c[3]))
print("深拷贝对象 :",d, id(d), id(d[2]),  id(d[3]))


'''
源对象     : ['11', '22', ('a', 'b'), [1, 2]] 2280496186568 2280495395848 2280496187784
赋值对象   : ['11', '22', ('a', 'b'), [1, 2]] 2280496186568 2280495395848 2280496187784
浅拷贝对象 : ['11', '22', ('a', 'b'), [1, 2]] 2280496303176 2280495395848 2280496187784
深拷贝对象 : ['11', '22', ('a', 'b'), [1, 2]] 2280496303240 2280495395848 2280496302920
################
源对象     : ['11', '22', ('a', 'b'), [1, 2, 3]] 2280496186568 2280495395848 2280496187784
赋值对象   : ['11', '22', ('a', 'b'), [1, 2, 3]] 2280496186568 2280495395848 2280496187784
浅拷贝对象 : ['11', '22', ('a', 'b'), [1, 2, 3]] 2280496303176 2280495395848 2280496187784
深拷贝对象 : ['11', '22', ('a', 'b'), [1, 2]] 2280496303240 2280495395848 2280496302920
[Finished in 0.8s]
'''

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值