python中的深拷贝和浅拷贝


数字和字符串、元组,不能改变对象本身,只能改变引用的指向,称为不可变数据对象(immutable object)。

列表、字典、集合可以通过引用其元素,改变对象自身(in-place change)。这种对象类型,称为可变数据对象(mutable object)。

赋值

赋值来的对象就是完完全全的原始对象,id值也和原始对象相同,实际上新对象就是指向原对象的引用,只是叫的名字不同了

数字拷贝

多个引用指向同一个对象,如果一个引用值发生变化,那么实际上是让这个引用指向一个新的引用, 并不影响其他的引用的指向。

>>> a = 5
>>> b = a
>>> print(id(a), id(b))
140442364586016 140442364586016
>>> a = a + 2	# a指向一个新的引用
>>> print(a, b)
7 5
>>> print(id(a), id(b))
140442364586080 140442364586016

字典拷贝

>>> a = {1:[1, 2, 3]}
>>> b = a
>>> a
{1: [1, 2, 3]}
>>> b
{1: [1, 2, 3]}
>>> print(id(a), id(b))
140442364973920 140442364973920

>>> a[2] = "No.2"
>>> a
{1: [1, 2, 3], 2: 'No.2'}
>>> b
{1: [1, 2, 3], 2: 'No.2'}

>>> a[1].append(4)
>>> a
{1: [1, 2, 3, 4], 2: 'No.2'}
>>> b
{1: [1, 2, 3, 4], 2: 'No.2'}

>>> a[2] = 'two'
>>> a
{1: [1, 2, 3, 4], 2: 'two'}
>>> b
{1: [1, 2, 3, 4], 2: 'two'}

列表拷贝

>>> a = [1, 2, 3, ['a', 'b']]
>>> b = a
>>> id(a)
140442230313800
>>> id(b)
140442230313800

>>> a.append(4)
>>> a
[1, 2, 3, ['a', 'b'], 4]
>>> b
[1, 2, 3, ['a', 'b'], 4]

>>> a[0] = 'a'
>>> a
['a', 2, 3, ['a', 'b'], 4]
>>> b
['a', 2, 3, ['a', 'b'], 4]

>>> a[3].append('c')
>>> a
['a', 2, 3, ['a', 'b', 'c'], 4]
>>> b
['a', 2, 3, ['a', 'b', 'c'], 4]

数字、字符串和元组是不可变对象,所以不能直接改变对象本身,只能重新给变量指向新的引用

浅拷贝

浅拷贝和深拷贝只会发生在容器类型里面包含其他可变容器类型的情况

浅拷贝可能会造成改变拷贝之后的值,同时也会改变原来的值

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

修改新对象中可变数据对象(如:列表、字典、集合)的值时,原对象也会相应改变,因为此时是在原来的引用上面直接进行修改,而新对象和原对象的子元素引用是相同的,所以原对象中的值也会跟着改变

修改新对象中不可变数据对象(如:数字、字符串、元组)的值时,原对象不会相应改变,因为改变不可变数据对象实际上是指向了一个新的引用

字典浅拷贝

修改可变数据对象不会改变指向的引用(地址),而是直接改变引用对象的值

>>> a = {"name":"sc", "score":[80, 90, 100]}
>>> b = a.copy()	

a.copy() 是将a中每个元素的地址拷贝到b中,所以b中的元素的地址跟a里面的元素的地址是一样的

>>> id(b["score"])
140017845519560
>>> id(a["score"])
140017845519560

>>> id(a["name"])
140017987252440
>>> id(b["name"])
140017987252440

但是b的地址和a的地址不一样,因为还是会给b新开辟一个内存空间

>>> id(a)
140017852211848
>>> id(b)
140017852116856
>>> b["score"].append(110)
>>> b
{'name': 'sc', 'score': [80, 90, 100, 110]}
>>> a
{'name': 'sc', 'score': [80, 90, 100, 110]}

修改不可变数据对象的值实际上是指向一个新的引用(地址)

>>> a = {"name":"sc", "score":(80, 90, 100)}
>>> b = a.copy() 
>>> a
{'name': 'sc', 'score': (80, 90, 100)}
>>> b
{'name': 'sc', 'score': (80, 90, 100)}

>>> b['score'] = (80, 90, 100, 110)
>>> b
{'name': 'sc', 'score': (80, 90, 100, 110)}
>>> a
{'name': 'sc', 'score': (80, 90, 100)}

>>> a['name'] = 'sanchuang'
>>> a
{'name': 'sanchuang', 'score': (80, 90, 100)}
>>> b
{'name': 'sc', 'score': (80, 90, 100, 110)}

浅拷贝只会拷贝第一层对象的地址

img

列表浅拷贝

>>> a = ['a', 'b', [1, 2, 3]]
>>> b = a.copy()
>>> a
['a', 'b', [1, 2, 3]]
>>> b
['a', 'b', [1, 2, 3]]
# b[2]是列表,改变新对象中列表中的值,原对象也会随着改变
>>> b[2][1] = 10
>>> b
['a', 'b', [1, 10, 3]]
>>> a
['a', 'b', [1, 10, 3]]

>>> a[2].append(4)
>>> a
['a', 'b', [1, 10, 3, 4]]
>>> b
['a', 'b', [1, 10, 3, 4]]
# a[0]是字符串,不可变数据对象,改变a[0]的值,b[0]不会改变
>>> a[0] = 'c'
>>> a
['c', 'b', [1, 10, 3, 4]]
>>> b
['a', 'b', [1, 10, 3, 4]]

使用*复制元素时,得到的每一个元素都与原元素指向同一个引用

>>> lst = [[]]*3
>>> lst
[[], [], []]
>>> lst[0]
[]
# 因为lst[0]、lst[1]、lst[2]指向同一个地址,所以改变任意一个子列表的值,其他子列表都会随着改变
>>> lst[0].append(1)
>>> lst
[[1], [1], [1]]
>>> id(lst[0])
140017845557832
>>> id(lst[1])
140017845557832
>>> id(lst[2])
140017845557832
>>> str = 'a'*3
>>> str
'aaa'
>>> id(str[0])
140442365736304
>>> id(str[1])
140442365736304
>>> id(str[2])
140442365736304

深拷贝

递归拷贝所有层的数据

深拷贝就是拷贝每一层地址

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

img

只有copy.deepcopy()拷贝的是深拷贝,其他的都是浅拷贝

>>> import copy
>>> b = copy.deepcopy(a)	# 进行深拷贝
>>> a
{'name': 'sc', 'score': [80, 90, 100, 110]}
>>> b
{'name': 'sc', 'score': [80, 90, 100, 110]}

>>> id(a["score"])
140017845519560
>>> id(b["score"])
140017845555912
>>> b["score"].append(120)
>>> b
{'name': 'sc', 'score': [80, 90, 100, 110, 120]}
>>> a
{'name': 'sc', 'score': [80, 90, 100, 110]}

但是深拷贝中新对象和原对象中子元素为字符串的地址指向是相同的,因为字符串有驻留区

>>> id(a['name'])
140442230296112
>>> id(b['name'])
140442230296112
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值