Python开发【第二章】:深浅拷贝剖析
2016-08-01 11:28
543 查看
Python深浅拷贝剖析
Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果。下面本文就通过简单的例子介绍一下这些概念之间的差别。
一、对象赋值
创建列表变量Alex,变量包含子列表,通过变量Alex给变量lzl赋值,然后对变量Alex的元素进行修改,此时lzl会有什么变化呢?让我们通过内存地址分析两者的变化
# 对象赋值 import copy #import调用copy模块 Alex = ["Alex", 28, ["Python", "C#", "JavaScript"]] lzl = Alex #直接赋值 # 修改前打印 print(id(Alex)) print(Alex) print([id(adr) for adr in Alex]) # 输出: 7316664 # ['Alex', 28, ['Python', 'C#', 'JavaScript']] # [2775776, 1398430400, 7318024] print(id(lzl)) print(lzl) print([id(adr) for adr in lzl]) # 输出: 7316664 # ['Alex', 28, ['Python', 'C#', 'JavaScript']] # [2775776, 1398430400, 7318024] # 对变量进行修改 Alex[0]='Mr.Wu' Alex[2].append('CSS') print(id(Alex)) print(Alex) print([id(adr) for adr in Alex]) # 输出: 7316664 # ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']] # [5170528, 1398430400, 7318024] print(id(lzl)) print(lzl) print([id(adr) for adr in lzl]) # 输出: 7316664 # ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']] # [5170528, 1398430400, 7318024]
通过上面的代码做出如下两图并进行分析:
1、首先,创建了一个名为Alex的变量,这个变量指向一个list列表,从第一张图中可以看到列表中元素的地址(每次运行,结果可能不同)。然后通过变量Alex给变量lzl进行赋值,变量lzl指向Alex指向的内存地址(7316664),所有可以理解为,Python中,对象的赋值都是进行对象引用(内存地址)的传递,被赋值的变量并没有开辟新内存,两个变量共用一个内存地址
2、第二张图中,由于Alex和lzl指向同一个对象(内存地址),所以对Alex的任何修改都会体现在lzl上。这里需要注意的一点是,str是不可变类型,所以当修改元素Alex为Mr.Wu时,内存地址由2775776变为了5170528,list是可变类型,元素['Python', 'C#', 'JavaScript', 'CSS']修改完后,内存地址仍然是7318024,没有发生改变
---------------------------------------------------------------------------------------
二、浅拷贝
创建列表变量Alex,变量包含子列表,通过copy模块的浅拷贝函数copy()对变量Alex进行拷贝,当对Alex进行操作时,此时lzl会如何变化?
# 浅拷贝 import copy #import调用copy模块 Alex = ["Alex", 28, ["Python", "C#", "JavaScript"]] lzl = copy.copy(Alex) #通过copy模块里面的浅拷贝函数copy() # 修改前打印 print(id(Alex)) print(Alex) print([id(adr) for adr in Alex]) # 输出: 10462472 # ['Alex', 28, ['Python', 'C#', 'JavaScript']] # [5462752, 1359960768, 10463232] print(id(lzl)) print(lzl) print([id(adr) for adr in lzl]) # 输出: 10201848 # ['Alex', 28, ['Python', 'C#', 'JavaScript']] # [5462752, 1359960768, 10463232] # 对变量进行修改 Alex[0]='Mr.Wu' Alex[2].append('CSS') print(id(Alex)) print(Alex) print([id(adr) for adr in Alex]) # 输出: 10462472 # ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']] # [10151264, 1359960768, 10463232] print(id(lzl)) print(lzl) print([id(adr) for adr in lzl]) # 输出: 10201848 # ['Alex', 28, ['Python', 'C#', 'JavaScript', 'CSS']] # [5462752, 1359960768, 10463232]
通过上面的代码做出如下两图并进行分析:
1、依然使用一个Alex变量,指向一个list类型的对象,list包含一个子list
2、然后,通过copy模块里面的浅拷贝函数copy(),对Alex指向的对象进行浅拷贝,然后浅拷贝生成的新对象赋值给lzl变量,注意此时变量lzl新建了一块内存(10201848),此内存记录了list中元素的地址,对于list中的元素,浅拷贝就会使用原始元素的引用(内存地址)
3、当对Alex进行修改的时候,由于list中第一个元素“Alex”(str)为不可变类型,所以进行修改的后,第一个元素对应的地址变为了10151264。由于list中第三个元素['Python', 'C#', 'JavaScript'](list)为可变类型,修改后地址没有变化。所以最后变量lzl和变量Alex只是第一个元素不一样。
-----------------------------------------------------------------------------------------
注:当我们使用下面的操作的时候,会产生浅拷贝的效果:
使用切片[:]操作
使用工厂函数(如list/dir/set)
使用copy模块中的copy()函数
三、深拷贝
创建列表变量Alex,变量包含子列表,通过copy模块的深拷贝函数deepcopy()对变量Alex进行拷贝,当对Alex进行操作时,此时lzl会如何变化?
#!/usr/bin/env python # -*- coding:utf-8 -*- #-Author-Lian # 深拷贝 import copy #import调用copy模块 Alex = ["Alex", 28, ["Python", "C#", "JavaScript"]] lzl = copy.deepcopy(Alex) #通过copy模块里面的深拷贝函数deepcopy() # 修改前打印 print(id(Alex)) print(Alex) print([id(adr) for adr in Alex]) # 输出: 6202712 # ['Alex', 28, ['Python', 'C#', 'JavaScript']] # [4086496, 1363237568, 6203472] print(id(lzl)) print(lzl) print([id(adr) for adr in lzl]) # 输出: 6203032 # ['Alex', 28, ['Python', 'C#', 'JavaScript']] # [4086496, 1363237568, 6203512] # 对变量进行修改 Alex[0]='Mr.Wu' Alex[2].append('CSS') print(id(Alex)) print(Alex) print([id(adr) for adr in Alex]) # 输出: 6202712 # ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']] # [5236064, 1363237568, 6203472] print(id(lzl)) print(lzl) print([id(adr) for adr in lzl]) # 输出: 6203032 # ['Alex', 28, ['Python', 'C#', 'JavaScript']] # [4086496, 1363237568, 6203512]
通过上面深拷贝的代码做出如下两图并进行分析:
1、依然使用一个Alex变量,指向一个list类型的对象,list包含一个子list
2、然后,通过copy模块里面的深拷贝函数deepcopy(),对Alex指向的对象进行深拷贝,然后深拷贝生成的新对象赋值给lzl变量。跟浅拷贝一样,此时变量lzl依然新建了一块内存(6203032),此内存记录了list中元素的地址。但是,对于list中的元素,深拷贝不是简单的使用原始元素的引用(内存地址),对于list第三个元素(['Python', 'C#', 'JavaScript'])重新生成了一个地址(6203512),此时两个变量的第三个元素的内存引用地址不同
3、当对Alex进行修改的时候,由于list中第一个元素“Alex”(str)为不可变类型,所以进行修改的后,第一个元素对应的地址变为了5236064。虽然list中第三个元素['Python', 'C#', 'JavaScript'](list)为可变类型,修改后不会产生新的地址,但是由于Alex和lzl在第三个元素引用的本就不同,所有Alex的修改对lzl不会产生任何影响
-------------------------------------------------------------------------------------
其实,对于拷贝有一些特殊情况:
对于非容器类型(如数字、字符串、和其他'原子'类型的对象)没有拷贝这一说
也就是说,对于这些类型,"obj is copy.copy(obj)" 、"obj is copy.deepcopy(obj)"
如果元祖变量只包含原子类型对象,则不能深拷贝
相关文章推荐
- python开发学习-day03(set集合、collection系列 、深浅拷贝、函数)
- Python全栈开发之3、数据类型set补充、深浅拷贝与函数
- IOS开发(35)之iOS深浅拷贝
- iOS开发中表视图搜索栏实现中的Objective-C深浅拷贝问题
- python 深浅拷贝
- Python基础:深浅拷贝
- 3.python基础补充(集合,collection系列,深浅拷贝)
- Python中的整数对象(Python 源码剖析第二章)
- Python-Day3知识点——深浅拷贝、函数基本定义、内置函数
- python 深浅拷贝
- python 深浅拷贝
- 【Python】Python中的深浅拷贝
- IOS开发之iOS深浅拷贝
- C++与Python的深浅拷贝比较
- python 深浅拷贝 进阶
- python小白-day3 深浅拷贝
- iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题
- iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题
- Python中的深浅拷贝详解
- python基础系列(三)---set、collection、深浅拷贝