python的浅拷贝与深拷贝

1 可变对象与不可变对象

  在学习浅拷贝和深拷贝之前,应该先了解可变对象和不可变对象这两个概念,否则会对浅拷贝和深拷贝的使用感到迷惑。那什么是可变对象和不可变对象呢?

1.1 可变对象

  可变对象是指,一个对象的值被修改后,指向该对象的地址是不会发生变化的。
  在python中,可变对象有:List(列表)、Dictionary(字典)、Set(集合)。

# 以list为例
a = [1, 2, 3]
b = a  # 引用
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a))
print("b[0]的地址:", id(a))
b[0] = "1"
print("a:", a)
print("b:", b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a))
print("b[0]的地址:", id(a))
#a的地址: 1413812442632
#b的地址: 1413812442632
#a[0]的地址: 1413812442632
#b[0]的地址: 1413812442632
#a: ['1', 2, 3]
#b: ['1', 2, 3]
#a的地址: 1413812442632
#b的地址: 1413812442632
#a[0]的地址: 1413812442632
#b[0]的地址: 1413812442632

  可以看到b引用a(a的地址和b的地址是一样的),对b[0]进行修改后,它的地址并没有发生变化(a[0]和b[0]的地址依旧是一样的),因此对b[0]的值进行修改,a[0]也会改变。

1.2 不可变对象

  不可变对象是指,一个对象的值被修改后,指向该对象的地址是会发生变化的。
  在python中,不可变对象有:Number(数字)、String(字符串)、Tuple(元组)。

# 以tuple为例
a = 1
b = a	# 引用
print("a:", a)
print("b:", b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("----------")
b = 2
print("a:", a)
print("b:", b)
print("a的地址:", id(a))
print("b的地址:", id(b))
#a: 1
#b: 1
#a的地址: 1482910464
#b的地址: 1482910464
#----------
#a: 1
#b: 2
#a的地址: 1482910464
#b的地址: 1482910496

  可以看到b引用a(a的地址和b的地址是一样的)。对b进行修改后,b的地址发生了发生变化,a的地址没变。

2 浅拷贝与深拷贝

  好的,介绍完可变对象和不可变对象,接下来就开始介绍浅拷贝和深拷贝。

2.1 浅拷贝

  浅拷贝:拷贝父对象(外层数据),会开辟一个新的内存空间存放拷贝得到的数据;不会拷贝对象的内部的子对象(内层数据),只是引用。
  但是对于浅拷贝,存在两种情况:

2.1.1 浅拷贝的第一种情况

  第一种情况是浅拷贝目标的父对象是可变对象,在这种情况下,会拷贝父对象,不会拷贝子对象。

import copy
a = [[1,2],3]
b = copy.copy(a)
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))
print("-----------b[0][0] = 2-----------")
b[0][0] = 2
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))
print("-----------b[1]=4-----------")
b[1]=4
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))

在这里插入图片描述
  从结果中可以看出对c进行浅拷贝后,c的地址和d的地址是不相同的。但c[0]和d[0]的地址以及c[1]和d[1]的地址是相同的。
  而修改b[0][0]和b[1]的值之后,地址的变化和可变对象和不可变对象中描述的原因是一样的,这里就不重复了。

2.1.2 浅拷贝的第二种情况

  第二种情况是浅拷贝目标的父对象是不可变对象,在这种情况下,不会拷贝父对象,也不会拷贝子对象。

import copy
a = "ab"
b = copy.copy(a)
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))
print("-----------b +=\"c\"-----------")
b +="c"
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))
print("b[2]的地址:", id(b[2]))

在这里插入图片描述
  从结果中可以看出对a进行浅拷贝后,a的地址和b的地址是依然相同的,并且a[0]和b[0]的地址以及a[1]和b[1]的地址是相同的。
  在进行b +="c"操作后,由于b的父对象是不可变对象,所以b地址发生了变化。但b内部的子对象的地址没有改变。

2.2 深拷贝

  深拷贝:拷贝父对象(外层数据),会开辟一个新的内存空间存放拷贝得到的数据;同时拷贝对象的内部的子对象(内层数据),会开辟一个新的内存空间存放拷贝得到的数据。即深拷贝得到的对象和原始对象不会再有任何联系。
  但是对于深拷贝,存在两种情况:

2.2.1 深拷贝的第一种情况

  第一种情况是深拷贝目标的父对象是可变对象,在这种情况下,会拷贝父对象,也会拷贝子对象。

import copy
a = [[1,2],3]
b = copy.deepcopy(a)
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))
print("-----------b[0][0] = 2-----------")
b[0][0] = 2
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))
print("-----------b[1]=4-----------")
b[1]=4
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))

在这里插入图片描述
  对a进行深拷贝后,b的地址和a的地址是不相同的,a[0]的地址和b[0]的地址也是不相同的,这说明它们都开辟和新的内存空间来存放,但是a[1]和b[1]的地址是相同的,因为a[1]是不可变元素。

2.2.2 深拷贝的第二种情况

  第二种情况是深拷贝目标的父对象是不可变对象,在这种情况下,不会拷贝父对象,但会拷贝子对象。

import copy
a = ([1,2],3)
b = copy.deepcopy(a)
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))
print("-----------b[0][0] = 2-----------")
b[0][0] = 2
print(a,b)
print("a的地址:", id(a))
print("b的地址:", id(b))
print("a[0]的地址:", id(a[0]))
print("b[0]的地址:", id(b[0]))
print("a[1]的地址:", id(a[1]))
print("b[1]的地址:", id(b[1]))

在这里插入图片描述
因为a的父对象与子对象a[1]都是不可变对象,所以即使是深拷贝,地址依然不会变,和b和b[1]相同,即没有开辟新的内存空间来存储。但是a[0]是可变对象,因此在深拷贝下会开辟新的内存空间来存储

3 总结

  浅拷贝只会拷贝父对象,而深拷贝会同时拷贝父对象和子对象。
  对于不可变对象,无论是父对象还是子对象,无论是进行深拷贝还是浅拷贝,都没有进行实际上的拷贝,只有它的值被改变才会开辟一个新的内存空间来存储新的值。
  对于可变对象,当是父对象,无论进行浅拷贝还是深拷贝,实际上都会进行拷贝;当是子对象是,只有进行深拷贝才会进行实际上的拷贝。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值