Python深浅拷贝剖析

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

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

一、对象赋值

创建列表变量Alex,变量包含子列表,通过变量Alex给变量lzl赋值,然后对变量Alex的元素进行修改,此时lzl会有什么变化呢?让我们通过内存地址分析两者的变化

  1. # 对象赋值
  2. import copy #import调用copy模块
  3.  
  4. Alex = ["Alex", 28, ["Python", "C#", "JavaScript"]]
  5. lzl = Alex #直接赋值
  6.  
  7. # 修改前打印
  8. print(id(Alex))
  9. print(Alex)
  10. print([id(adr) for adr in Alex])
  11. # 输出: 7316664
  12. # ['Alex', 28, ['Python', 'C#', 'JavaScript']]
  13. # [2775776, 1398430400, 7318024]
  14. print(id(lzl))
  15. print(lzl)
  16. print([id(adr) for adr in lzl])
  17. # 输出: 7316664
  18. # ['Alex', 28, ['Python', 'C#', 'JavaScript']]
  19. # [2775776, 1398430400, 7318024]
  20.  
  21. # 对变量进行修改
  22. Alex[0]='Mr.Wu'
  23. Alex[2].append('CSS')
  24. print(id(Alex))
  25. print(Alex)
  26. print([id(adr) for adr in Alex])
  27. # 输出: 7316664
  28. # ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
  29. # [5170528, 1398430400, 7318024]
  30. print(id(lzl))
  31. print(lzl)
  32. print([id(adr) for adr in lzl])
  33. # 输出: 7316664
  34. # ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
  35. # [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会如何变化?

  1. # 浅拷贝
  2. import copy #import调用copy模块
  3.  
  4. Alex = ["Alex", 28, ["Python", "C#", "JavaScript"]]
  5. lzl = copy.copy(Alex) #通过copy模块里面的浅拷贝函数copy()
  6.  
  7. # 修改前打印
  8. print(id(Alex))
  9. print(Alex)
  10. print([id(adr) for adr in Alex])
  11. # 输出: 10462472
  12. # ['Alex', 28, ['Python', 'C#', 'JavaScript']]
  13. # [5462752, 1359960768, 10463232]
  14. print(id(lzl))
  15. print(lzl)
  16. print([id(adr) for adr in lzl])
  17. # 输出: 10201848
  18. # ['Alex', 28, ['Python', 'C#', 'JavaScript']]
  19. # [5462752, 1359960768, 10463232]
  20.  
  21. # 对变量进行修改
  22. Alex[0]='Mr.Wu'
  23. Alex[2].append('CSS')
  24. print(id(Alex))
  25. print(Alex)
  26. print([id(adr) for adr in Alex])
  27. # 输出: 10462472
  28. # ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
  29. # [10151264, 1359960768, 10463232]
  30. print(id(lzl))
  31. print(lzl)
  32. print([id(adr) for adr in lzl])
  33. # 输出: 10201848
  34. # ['Alex', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
  35. # [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会如何变化?

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. #-Author-Lian
  4. # 深拷贝
  5. import copy #import调用copy模块
  6.  
  7. Alex = ["Alex", 28, ["Python", "C#", "JavaScript"]]
  8. lzl = copy.deepcopy(Alex) #通过copy模块里面的深拷贝函数deepcopy()
  9.  
  10. # 修改前打印
  11. print(id(Alex))
  12. print(Alex)
  13. print([id(adr) for adr in Alex])
  14. # 输出: 6202712
  15. # ['Alex', 28, ['Python', 'C#', 'JavaScript']]
  16. # [4086496, 1363237568, 6203472]
  17. print(id(lzl))
  18. print(lzl)
  19. print([id(adr) for adr in lzl])
  20. # 输出: 6203032
  21. # ['Alex', 28, ['Python', 'C#', 'JavaScript']]
  22. # [4086496, 1363237568, 6203512]
  23.  
  24. # 对变量进行修改
  25. Alex[0]='Mr.Wu'
  26. Alex[2].append('CSS')
  27. print(id(Alex))
  28. print(Alex)
  29. print([id(adr) for adr in Alex])
  30. # 输出: 6202712
  31. # ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
  32. # [5236064, 1363237568, 6203472]
  33. print(id(lzl))
  34. print(lzl)
  35. print([id(adr) for adr in lzl])
  36. # 输出: 6203032
  37. # ['Alex', 28, ['Python', 'C#', 'JavaScript']]
  38. # [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开发【第二章】:Python深浅拷贝剖析的更多相关文章

  1. [Python笔记][第二章Python序列-复杂的数据结构]

    2016/1/27学习内容 第二章 Python序列-复杂的数据结构 堆 import heapq #添加元素进堆 heapq.heappush(heap,n) #小根堆堆顶 heapq.heappo ...

  2. [Python笔记][第二章Python序列-tuple,dict,set]

    2016/1/27学习内容 第二章 Python序列-tuple tuple创建的tips a_tuple=('a',),要这样创建,而不是a_tuple=('a'),后者是一个创建了一个字符 tup ...

  3. [python笔记][第二章Python序列-list]

    2016/1/27学习内容 第二章 Python序列-list list常用操作 list.append(x) list.extend(L) list.insert(index,x) list.rem ...

  4. 路飞学城-Python开发-第二章

    ''' 数据结构: menu = { '北京':{ '海淀':{ '五道口':{ 'soho':{}, '网易':{}, 'google':{} }, '中关村':{ '爱奇艺':{}, '汽车之家' ...

  5. Python笔记·第二章—— Python的编码问题(一)

    一.什么是编码 可以说,计算机是一个即聪明又笨蛋的家伙.说它聪明,是因为他可以做很多事情,它的强大无需多说,大家应该都有所了解以及感受.但是为什么说它又是个笨蛋呢,因为我们在电脑上写出的每一个字,保存 ...

  6. Python自学:第二章 Python之禅

    >>print import <Python之禅>,提姆·彼得斯著 美胜于丑. 显式优于隐式. 简单胜于复杂. 复杂总比复杂好. 平的比嵌套的好. 稀疏胜于稠密. 可读性计数. ...

  7. Python开发【第二章】:深浅拷贝剖析

    Python深浅拷贝剖析 Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果. 下面本文就通过简单的例子介绍一下这些概念之间的差别. 一.对象赋值 ...

  8. 第二章Python入门

    第二章 Python入门 2.1.简介 Python是著名的"龟叔"(Guido van Rossum)在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言 Pytho ...

  9. visual studio 2015 搭建python开发环境,python入门到精通[三]

    在上一篇博客Windows搭建python开发环境,python入门到精通[一]很多园友提到希望使用visual studio 2013/visual studio 2015 python做demo, ...

随机推荐

  1. 163源报错Hash Sum mismatch 解决方法

    Ubuntu server 用的163的源,报错: W: Failed to fetch http://mirrors.163.com/ubuntu/dists/precise-updates/mai ...

  2. 修改文档框架:word-多级列表与标题样式相结合

    转自:http://blog.sina.com.cn/s/blog_6721f25c0100nuf0.html 设置标题的时候希望出现多标题并且自动编号的标题,如下1.     XXXXXXXXXXX ...

  3. eclipse 高亮配对的括号

    在编辑代码框右键->preference,直接就可以看到Matching brackets highlights

  4. Linux远程传输命令之scp使用方法

      首先用pwd命令确定文件全路径   1.获取远程服务器上的文件 cykdeMacBook-Pro:~ cyk$ scp cyk@10.211.55.5:/home/cyk/Desktop/hi.t ...

  5. 【POJ】3243 Clever Y

    http://poj.org/problem?id=3243 题意:求$a^y \equiv b \pmod{p}$最小的$y$.(0<=x, y, p<=10^9) #include & ...

  6. 【POJ】2096 Collecting Bugs

    http://poj.org/problem?id=2096 题意:s个系统n种bug,每天找出一个bug,种类的概率是1/n,系统的概率是1/s.问:每个系统至少找出一个bug:每种类的bug都被找 ...

  7. [BZOJ2788][Poi2012]Festival

    2788: [Poi2012]Festival Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 187  Solved: 91[Submit][Statu ...

  8. linux修改系统编码

    Windows的默认编码为GBK,Linux的默认编码为UTF-8.在Windows下编辑的中文,在Linux下显示为乱码.一种方法是在windows进行转码,比如使用ue工具在文件-->转换 ...

  9. 20145330《Java程序设计》第四周学习总结

    20145330<Java程序设计>第四周学习总结 一周两章新知识的自学与理解真的是很考验和锻炼我们,也对前面几章我们的学习进行了检测,遇到忘记和不懂的知识就再复习前面的,如此巩固也让我对 ...

  10. eclipse配置PHP开发环境

    下载 http://www.oracle.com/technetwork/java/javase/downloads/index.html下载JDK,Eclipse 安装需要JDK环境:http:// ...