图解Python深拷贝和浅拷贝

Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果。

下面本文就通过简单的例子介绍一下这些概念之间的差别。

对象赋值

直接看一段代码:

will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = will
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

代码的输出为:

下面来分析一下这段代码:

  • 首先,创建了一个名为will的变量,这个变量指向一个list对象,从第一张图中可以看到所有对象的地址(每次运行,结果可能不同)
  • 然后,通过will变量对wilber变量进行赋值,那么wilber变量将指向will变量对应的对象(内存地址)由于will和wilber指向同一个对象,所以对will的任何修改都会体现在wilber上

  • 这里需要注意的一点是,str是不可变类型,所以当修改的时候会替换旧的对象,产生一个新的地址39758496(int也是这种情况)

浅拷贝(等价于切片【:】)

下面就来看看浅拷贝的结果:

import copy

will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.copy(will)

print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

代码结果为:

分析一下这段代码:

通过copy模块里面的浅拷贝函数copy(),对will指向的对象进行浅拷贝,然后浅拷贝生成的新对象赋值给wilber变量

当对will进行修改的时候,两个列表指向不同的地址,但两个列表中嵌套的列表却是相同的类表。修改其中一个列表不会影响另一个列表,但修改一个列表中嵌套的那个列表是就会使另一个发生改变

总结一下,当我们使用下面的操作的时候,会产生浅拷贝的效果:

  • 使用切片[:]操作
  • 使用工厂函数(如list/dir/set)
  • 使用copy模块中的copy()函数

深拷贝

最后来看看深拷贝:

import copy

will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.deepcopy(will)

print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]

代码的结果为:

分析一下这段代码:

通过copy模块里面的深拷贝函数deepcopy(),对will指向的对象进行深拷贝,然后深拷贝生成的新对象赋值给wilber变量

当对进行修改的时,两个类表指向及其里面的元素指向不同的地址,对任何一个列表的操作不会影响另一个。

(但是int那个28不是指向相同的地址吗,这个见拷贝的特殊情况,但是修改任意一个的值不会影响另一个)

 

拷贝的特殊情况

对于非容器类型(如数字、字符串、和其他'原子'类型的对象)没有拷贝这一说,即若两个变量的值相同,就是指向相同的地址

程序(int):

x=5

y=5

z=6

print(id(x),id(y),id(z))

print(x==y)

print(x is y)

x=6

print(id(x),id(y),id(z))

x=7

print(id(x),id(y),id(z))

输出:

(34319432, 34319432, 34319420)
True
True
(34319420, 34319432, 34319420)
(34319408, 34319432, 34319420)

解释:执行x=5开辟了内存空间并填入5,然后执行y=5时就去表里(编译原理的知识)查找,发现有5这个常量,就指向x对应的空间,在之后执行z=6时,先去表里查询发现没有6,就新开辟空间。然后x=6,表里有6这个常量,就去指向z对应的空间,再次修改x=7时,因为表里没有7这个常量,就新开辟内存空间,并把7填入该空间,然后令x指向该空间

即x为原子变量,每次执行x=时都从新分配空间,这个区别于非原子变量

  • 只包含原子类型(int,float,str)对象,则不能深拷贝,看下面的例子

    

总结

本文介绍了对象的赋值和拷贝,以及它们之间的差异:

  • Python中对象的赋值都是进行对象引用(内存地址)传递
  • 使用copy.copy(),可以进行对象的浅拷贝,它复制了对象,但对于对象中的元素,依然使用原始的引用.
  • 如果需要复制一个容器对象,以及它里面的所有元素(包含元素的子元素),可以使用copy.deepcopy()进行深拷贝
  • 对于非容器类型(如数字、字符串、和其他'原子'类型的对象)没有被拷贝一说

 

  • 作者:田小计划

    出处:http://www.cnblogs.com/wilber2013/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

     

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值