Python高级:细说Python浅拷贝和深拷贝

0.说明 

        先看看浅拷贝的概念:

  • 浅拷贝:对一个对象进行浅拷贝其实是新创建了一个类型跟原对象一样,其内容还是原来对象元素的引用,换句话说,这个拷贝的对象本身是新的,但是它的内容不是

        序列类型对象的浅拷贝是默认类型拷贝,有以下几种实现方式:

  • 完全切片操作:下面操作会有
  • 利用工厂函数:比如list()、dict()等
  • 使用copy模块的copy()函数

        其实如果是真正理解了Python对象或者说理解了可变对象和不可变对象,再根据上面的理论知识,浅拷贝和深拷贝基本上算是比较好的掌握了。所以这里不按照书上(指的是《Python核心编程》)的思路来进行总结,当然书上的例子作为入门也是非常不错的。下面给出三个例子,如果都可以理解,那么对Python浅拷贝和深拷贝的掌握到这个程度也就可以了。


1.第一个例子:列表中的元素都是原子类型,即不可变对象

        


1

2

3

4

5

6

7

8

9

10

11

12

>>> person = ['age'20]

>>> xpleaf = person[:]  #浅拷贝

>>> cl = list(person)      #浅拷贝

>>> [id(x) for in person, xpleaf, cl]   #虽然是浅拷贝,但是其实也是生成了新的对象

[140205544875144140205544893688140205544996232]

>>> [id(x) for in person]

[14020554502123232419728]

>>> [id(x) for in xpleaf]

[14020554502123232419728]

>>> [id(x) for in cl]

[14020554502123232419728]

#但是可以看到列表中的元素的还是原来对象元素的引用

        上面做了浅拷贝的操作,然后下面修改两个浅拷贝的值:


1

2

3

4

5

6

7

8

9

10

>>> xpleaf[1= 22

>>> cl[1= 21

>>> person, xpleaf, cl

(['age'20], ['age'22], ['age'21])

>>> [id(x) for in person]

[14020554502123232419728]

>>> [id(x) for in xpleaf]

[14020554502123232419680]

>>> [id(x) for in cl]

[14020554502123232419704]

        修改了两个浅拷贝的值,然后发现内容并没有相互影响,而且后来的id值也发生改变了,怎么会这样?不要忘了,列表中的元素都是不可变对象,修改不可变对象的值,其实就相当于是新生成了一个该对象,然后让列表元素重新指向新生成的不可变对象,在这里是数字对象。

        理解这个例子本身并不难,但需要对Python对象和序列类型本身有一定的理解。


2. 第二个例子:列表中包含容器类型变量,即可变对象

        


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

>>> person = ['name', ['age'20]]

>>> xpleaf = person[:]

>>> cl = list(person)

>>> person, xpleaf, cl

(['name', ['age'20]], ['name', ['age'20]], ['name', ['age'20]])

>>> [id(x) for in person, xpleaf, cl]

[140205544995944140205544893688140205544875144]

# 查看大列表的元素id值

>>> [id(x) for in person, xpleaf, cl]

[140205544996160140205544875144140205544996520]

>>> [id(x) for in person]

[140205546176112140205544995944]

>>> [id(x) for in xpleaf]

[140205546176112140205544995944]

>>> [id(x) for in cl]

[140205546176112140205544995944]

# 查看小列表的元素id值

>>> [id(x) for in person[1]]

[14020554502123232419680]

>>> [id(x) for in xpleaf[1]]

[14020554502123232419680]

>>> [id(x) for in cl[1]]

[14020554502123232419680]

        三个列表的第一个元素的id值都是一样的,这是引用传递,没有什么问题,跟第一个例子类似,因此修改这个值不会有什么问题。但注意看第二个元素,它是一个列表,可以肯定的是,三个列表中的两个元素的id也肯定是相同的,也是引用传递的道理,但现在关键是看第二个元素,也就是这个列表本身,三个大列表(指的是person这个列表)中的这三个小列表的id值都是一样的,于是,浅拷贝对于对象值的影响就会体现出来了,我们尝试去修改其中一个小列表中的值:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

>>> xpleaf[1][1= 22

>>> person, xpleaf, cl

(['name', ['age'22]], ['name', ['age'22]], ['name', ['age'22]])

>>> [id(x) for in person, xpleaf, cl]

[140205544995944140205544893688140205544875144]

# 查看大列表的元素id值

>>> [id(x) for in person]

[140205546176112140205544995944]

>>> [id(x) for in xpleaf]

[140205546176112140205544995944]

>>> [id(x) for in cl]

[140205546176112140205544995944]

# 查看小列表的元素id值

>>> [id(x) for in person[1]]

[14020554502123232419680]

>>> [id(x) for in xpleaf[1]]

[14020554502123232419680]

>>> [id(x) for in cl[1]]

[14020554502123232419680]

        可以看到问题就出来了,即对一个小列表进行修改,会影响到其它的小列表。我们先抛开所谓的浅拷贝,去思考这个问题本身:有可能不会影响其它小列表吗?肯定没有可能的,因为三个小列表的id都一样,三个小列表里的元素的id也一样,即其实这三个小列表是完全指向同一个对象的,因此,无论修改哪一个,肯定都会影响其它小列表的。

        这就是所谓浅拷贝出现的问题。


3.第三个例子:使用深拷贝来解决第二个例子出现的问题

        


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

>>> person = ['name', ['age'20]]

>>> xpleaf = person[:]

>>> from copy import deepcopy as dcp

>>> cl = dcp(person)

>>> person, xpleaf, cl

(['name', ['age'20]], ['name', ['age'20]], ['name', ['age'20]])

>>> [id(x) for in person, xpleaf, cl]

[140205544995944140205544893688140205544875144]

# 查看大列表的元素id值

>>> [id(x) for in person]

[140205546176112140205544996520]

>>> [id(x) for in xpleaf]

[140205546176112140205544996520]

>>> [id(x) for in cl]

[140205546176112140205544571320]

# 查看小列表的元素id值

>>> [id(x) for in person[1]]

[14020554502123232419728]

>>> [id(x) for in xpleaf[1]]

[14020554502123232419728]

>>> [id(x) for in cl[1]]

[14020554502123232419728]

       可以看到虽然是进行了深拷贝,但发现跟前面的其实并没有什么不同,下面我们再来修改其中一个小列表:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

>>> xpleaf[1][1= 22

>>> person, xpleaf, cl

(['name', ['age'22]], ['name', ['age'22]], ['name', ['age'20]])

# 查看大列表的元素id值

>>> [id(x) for in person]

[140205546176112140205544996520]

>>> [id(x) for in xpleaf]

[140205546176112140205544996520]

>>> [id(x) for in cl]

[140205546176112140205544571320]

# 查看小列表的元素id值

>>> [id(x) for in person[1]]

[14020554502123232419680]

>>> [id(x) for in xpleaf[1]]

[14020554502123232419680]

>>> [id(x) for in cl[1]]

[14020554502123232419728]

        此时可以看到,cl的小列表的第二个元素的id跟原来是一样的,但是xpleaf和person的小列表元素的id发生了改变,同时值也是我们修改的那样。那是因为xpleaf是person的浅拷贝,但是cl是person的深拷贝。

        这就是所谓的深拷贝。


4.第四个例子:检验

         其实只要理解了上面三个例子(这意味着对Python对象本身和序列类型本身也有比较深刻的理解),所以的浅拷贝和深拷贝也不是什么问题了。

        至于是否明白,可以参考下面这个例子:


1

2

3

4

5

6

7

8

9

10

11

12

13

>>> person = ['name', ('hobby', [12])]

>>> xpleaf = person[:]

>>> from copy import deepcopy as dcp

>>> cl = dcp(person)

>>> 

>>> xpleaf[0= 'xpleaf'

>>> cl[0= 'cl'

>>> person, xpleaf, cl

(['name', ('hobby', [12])], ['xpleaf', ('hobby', [12])], ['cl', ('hobby', [12])])

>>> 

>>> xpleaf[1][1][0= 'clyyh'

>>> person, xpleaf, cl

(['name', ('hobby', ['clyyh'2])], ['xpleaf', ('hobby', ['clyyh'2])], ['cl', ('hobby', [12])])

        如果对这个例子的输出觉得完全没有问题的,那么也就OK了!

        当然,肯定还有遗漏的地方,还望指出。

时间: 2024-10-19 17:25:16

Python高级:细说Python浅拷贝和深拷贝的相关文章

Python学习笔记之浅拷贝和深拷贝

在Python中对象的复制有三种一般的复制,浅拷贝,深拷贝,那么他们有什么区别呢 一般的复制 #encoding:utf-8 #定义一个嵌套集合 lista=[1,2,3,[4,5,6,[7,8,9]]] listb=lista #分别打印出 lista和listb的地址值 print id(lista) #4511103096 print id(listb) #4511103096 #修改lista中的内容,listb中的内容也会跟着修改 lista[0]=0 print lista #[0,

Python浅拷贝与深拷贝用法实例

  本文实例讲述了Python浅拷贝与深拷贝用法.分享给大家供大家参考.具体分析如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 >>> person=['name',['savings',100]] >>> hubby=person[:] >>> wifey=list(person) >>> [id(x) for x in person,hubby,wifey] [3074051788L, 3074061740

Python中的赋值、浅拷贝、深拷贝介绍_python

和很多语言一样,Python中也分为简单赋值.浅拷贝.深拷贝这几种"拷贝"方式. 在学习过程中,一开始对浅拷贝理解很模糊.不过经过一系列的实验后,我发现对这三者的概念有了进一步的了解. 一.赋值 赋值算是这三种操作中最常见的了,我们通过一些例子来分析下赋值操作: str例 复制代码 代码如下: >>> a = 'hello' >>> b = 'hello' >>> c = a >>> [id(x) for x in

探讨Python高级编程技巧及实例

前面我们讲了Python高级数据结构学习教程,现在我们来看看一些高级的Python设计结构和它们的使用方法. 在日常工作中,你可以根据需要选择合适的数据结构,例如对快速查找性的要求.对数据一致性的要求或是对索引的要求等,同时也可以将各种数据结构合适地结合在一起,从而生成具有逻辑性并易于理解的数据模型.Python的数据结构从句法上来看非常直观,并且提供了大量的可选操作.这篇指南尝试将大部分常用的数据结构知识放到一起,并且提供对其最佳用法的探讨. 推导式(Comprehensions) 如果你已经

Python高级数据结构学习教程

本文虽然不是很深入,不过实例比较多,学习Python高级数据结构的好基础教程. 数据结构的概念 数据结构的概念很好理解,就是用来将数据组织在一起的结构.换句话说,数据结构是用来存储一系列关联数据的东西.在Python中有四种内建的数据结构,分别是List.Tuple.Dictionary以及Set.大部分的应用程序不需要其他类型的数据结构,但若是真需要也有很多高级数据结构可供选择,例如Collection.Array.Heapq.Bisect.Weakref.Copy以及Pprint.本文将介绍

Python网络02 Python服务器进化

原文:Python网络02 Python服务器进化 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! **注意,在Python 3.x中,BaseHTTPServer, SimpleHTTPServer, CGIHTTPServer整合到http.server包,SocketServer改名为socketserver,请注意查阅官方文档. 在上一篇文章中(用socket写一个Python服务器),我们在不依赖框架和CGI的情况下

Python应用02 Python服务器进化

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! **注意,在Python 3.x中,BaseHTTPServer, SimpleHTTPServer, CGIHTTPServer整合到http.server包,SocketServer改名为socketserver,请注意查阅官方文档. 在上一篇文章中(用socket写一个Python服务器),我使用socket接口,制作了一个处理HTTP请求的Python服务器.任何一台装有

python-如何进行Python优化使得Python模块能够得到2倍性能提升

问题描述 如何进行Python优化使得Python模块能够得到2倍性能提升 目前要对一个含有Python模块和C++模块的项目(Python模块调用C++模块)进行优化,对方公司希望我们团队实现一个提升2倍python模块性能的优化编译器 求问应用什么技术可以使得python编译器能够获得2倍性能提升并且不存在C扩展性弱的问题?(目前pypy虽然有5倍,6性能提升但是C扩展弱不支持我这个项目) 目前我考虑 去修改pypy的C扩展性,牺牲pypy一部分的性能提升空间,来换取它对C扩展模块的支持,或

C# 浅拷贝与深拷贝区别 解惑篇

问题起源: 昨天被同事问到一个浅拷贝与深拷贝区别的问题,说实在的,记得在学校时在书在看过相关概念区别. 只是,那时的在校生,又有几个能对书本上那写的尽量让鬼都看不懂知识能清晰的理解呢. 工作后虽然也有用到Clone相关的内容,不过也仅是应用,基础的概念还是没去仔细的理解,以于后来DataTable内部那不深不浅的架构拷贝都混和这些概念混在一起了. 曾经的以为: 让得一年多以前,我去面试,被一个人问到浅拷贝与深拷贝区别,我答:浅拷贝就是拷贝就是拷贝架构,不包括值,深拷贝就是连值也一同拷贝. 当我答