当重新学习了计算机基础课程《数据结构和算法分析》后再来看这篇自己以前写的博文,发现错误百出。python内置数据类型之所以会有这些特性,归根结底是它采用的是传递内存地址的方式,而不是传递真实值的方式。list使用的是动态顺序存储方式,每一个下标位置存储的是实际值的内存地址,而不是值的本体。

大家都知道,在python中复制一个对象有多种方法,其中常用的是赋值、浅拷贝和深拷贝,这三者之间有哪些区别和哪些坑呢?

首先,定义一下:

  赋值:  a =1    b =a    a赋值给了b

  浅拷贝: a = []          b = a.copy()

      或者import copy             b = copy.copy(a)

  深拷贝:import copy   a = []   b = copy.deepcopy(a)

通常,我们使用id()方法来查看对象的内存地址。

python对不同类型对象所采取的策略是不一样的,所以我们必须区分不同的类型来进行分析:

(以下全部在python3.5.1环境中测试)

一、数字型

Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> a = 1
>>> b = a
>>> id(a),id(b)
(1630727984, 1630727984)
>>> a = 2
>>> id(a),id(b)
(1630728016, 1630727984)
>>> c = a.copy()
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    c = a.copy()
AttributeError: 'int' object has no attribute 'copy'
>>> improt copy
SyntaxError: invalid syntax
>>> import copy
>>> c = copy.copy(a)
>>> id(a),id(c)
(1630728016, 1630728016)
>>> a = 3
>>> id(a),id(c)
(1630728048, 1630728016)
>>> d = copy.deepcopy(a)
>>> id (a),id(d)
(1630728048, 1630728048)
>>> a =4
>>> id (a),id(d)
(1630728080, 1630728048)
>>> 

  从测试中我们可以看出,对于数字类型,不管是赋值还是深浅拷贝,一律都是指向同一块地址,而一旦a的值改变了则a重新指向新的内存空间,a,b,c,d成为完全独立的4个变量。一句话概括,对于数据类型随便怎么赋值拷贝,所有变量不会互相影响。(注意,数字类型没有copy()方法。)

二、字符串

>>> import copy
>>> a = 'hello'
>>> b = a
>>> a,b
('hello', 'hello')
>>> id(a,b)
Traceback (most recent call last):
  File "<pyshell#21>", line 1, in <module>
    id(a,b)
TypeError: id() takes exactly one argument (2 given)
>>> id(a),id(b)
(51179736, 51179736)
>>> a[0]
'h'
>>> a[0] = 'w'
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    a[0] = 'w'
TypeError: 'str' object does not support item assignment
>>> a = 'world'
>>> a,b
('world', 'hello')
>>> id(a),id(b)
(51179848, 51179736)
>>> del a,b
>>> a = 'hello'
>>> b = a.copy()
Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    b = a.copy()
AttributeError: 'str' object has no attribute 'copy'
>>> b = copy.copy(a)
>>> a,b
('hello', 'hello')
>>> id(a),id(b)
(51179736, 51179736)
>>> a = 'world'
>>> a,b
('world', 'hello')
>>> id(a),id(b)
(51179848, 51179736)
>>> del a,b
>>> a = 'hello'
>>> b = copy.deepcopy(a)
>>> a,b
('hello', 'hello')
>>> id(a),id(b)
(51179736, 51179736)
>>> a = 'world'
>>> a,b
('world', 'hello')
>>> id(a),id(b)
(51179848, 51179736)

字符串

  从测试中我们可以看出,字符串类型和数字类型一样,不管是赋值还是深浅拷贝,一律都是指向同一块地址,而一旦a的值改变了则a重新指向新的内存空间地址。一句话概括,对于字符串类型随便怎么赋值拷贝,所有变量不会互相影响。(注意,字符串类型同样没有copy()方法。)

>>> a = 'hello'
>>> b = a[:]
>>> a,b
('hello', 'hello')
>>> id(a),id(b)
(51179792, 51179792)
>>> a = 'world'
>>> a,b
('world', 'hello')
>>> id(a),id(b)
(51179904, 51179792)

字符串的切片赋值

  同样的,字符串的切片赋值和上面也是一样的效果。

三、列表

>>> a = [1,2,3,[1,2,3]]
>>> b = a
>>> a
[1, 2, 3, [1, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> a[0]=4
>>> a
[4, 2, 3, [1, 2, 3]]
>>> b
[4, 2, 3, [1, 2, 3]]
>>> id(a),id(b)
(51001160, 51001160)
>>> id(a[3]),id(b[3])
(50931208, 50931208)
>>> a[3][0]=5
>>> a
[4, 2, 3, [5, 2, 3]]
>>> b
[4, 2, 3, [5, 2, 3]]
>>> 

赋值

  可见,对于列表,赋值其实也是将新的变量指向同样的地址,a的变化会影响到b,b就相当于a的一个符号链接,两者基本等同。

>>> a = [1,2,3,[1,2,3]]
>>> b = a.copy()
>>> a
[1, 2, 3, [1, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> id(a),id(b)
(51006152, 50932488)
>>>
>>> a[0]=4
>>> a
[4, 2, 3, [1, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> id(a),id(b)
(51006152, 50932488)
>>> a[3][0]=5
>>> a
[4, 2, 3, [5, 2, 3]]
>>> b
[1, 2, 3, [5, 2, 3]]
>>> id(a[3]),id(b[3])
(3160392, 3160392)

列表的copy()方法

  对于列表的copy()方法,从一开始,b就有了和a不同的内存地址,因此对于a的最顶层次的元素的修改,影响不到b。但是!下一层次的修改却在b中得到了反映。这说明copy()方法是一个浅拷贝。

>>> import copy
>>> a = [1,2,3,[1,2,3]]
>>> b = copy.copy(a)
>>> a
[1, 2, 3, [1, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> id(a),id(b)
(51647752, 51647944)
>>> a[0] = 4
>>> a
[4, 2, 3, [1, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> a[3][0] = 5
>>> a
[4, 2, 3, [5, 2, 3]]
>>> b
[1, 2, 3, [5, 2, 3]]
>>> id(a[3]),id(b[3])
(51647688, 51647688)

copy模块的copy()方法

  从例子中可以看出,copy模块的copy()方法可上面的copy方法一样,都是浅拷贝。

>>> a = [1,2,3,[1,2,3]]
>>> b = copy.deepcopy(a)
>>> a
[1, 2, 3, [1, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> id(a),id(b)
(51647880, 51647752)
>>> a[0] = 4
>>> a
[4, 2, 3, [1, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> a[3][0] = 5
>>> a
[4, 2, 3, [5, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> id(a[3]),id(b[3])
(51542856, 51647688)

deepcopy()方法

  从例子中可以看出,copy模块的deepcopy()方法可以实现深层次的复制,达到完整复制出一份互不干扰的拷贝

>>> a = [1,2,3,[1,2,3]]
>>> b = a[:]
>>> a
[1, 2, 3, [1, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> id(a),id(b)
(51647688, 51647368)
>>> a[0] = 4
>>> a
[4, 2, 3, [1, 2, 3]]
>>> b
[1, 2, 3, [1, 2, 3]]
>>> a[3][0] = 5
>>> a
[4, 2, 3, [5, 2, 3]]
>>> b
[1, 2, 3, [5, 2, 3]]
>>> id(a[3]),id(b[3])
(51647752, 51647752)

切片方法

  大家知道,字符串、元组和列表有一种切片复制的方法,即b = a[:]。从上面的代码中,我们可以发现,切片其实也是一种浅拷贝。(注意:字典没有切片功能)

四、字典

>>> a = {'k1':1,'k2':2,'k3':{'m1':'a','m2':'b'}}
>>> b = a
>>> a
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> id(a),id(b)
(51561096, 51561096)
>>> a['k1'] = 2
>>> a
{'k1': 2, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 2, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> a['k3']['m1'] = 'c'
>>> a
{'k1': 2, 'k3': {'m1': 'c', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 2, 'k3': {'m1': 'c', 'm2': 'b'}, 'k2': 2}
>>> id(a['k3']['m1']),id(b['k3']['m1'])
(9525768, 9525768)

赋值

  从例子中可以见到,赋值的时候,两个字典指向同一个地址,b就是a的一个软链接,相当于别名。

>>> a = {'k1':1,'k2':2,'k3':{'m1':'a','m2':'b'}}
>>> b = a.copy()
>>> a
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> id(a),id(b)
(50977032, 51561096)
>>> a['k1'] = 2
>>> a
{'k1': 2, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> a['k3']['m1'] = 'c'
>>> a
{'k1': 2, 'k3': {'m1': 'c', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'c', 'm2': 'b'}, 'k2': 2}
>>> id(a['k3']['m1']),id(b['k3']['m1'])
(9525768, 9525768)

copy方法

  从例子中可以看到,字典的copy方法开辟了一块新的内存空间,但是这也是浅拷贝,只有最顶层的元素获得了独立,以下层次的元素依然指向同一个地址。

>>> a = {'k1':1,'k2':2,'k3':{'m1':'a','m2':'b'}}
>>> import copy
>>> b = copy.copy(a)
>>> a
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> id(a),id(b)
(51583560, 51583752)
>>> a['k1'] = 2
>>> a
{'k1': 2, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> a['k3']['m1'] = 'c'
>>> a
{'k1': 2, 'k3': {'m1': 'c', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'c', 'm2': 'b'}, 'k2': 2}
>>> id(a['k3']['m1']),id(b['k3']['m1'])
(9525768, 9525768)

copy模块的copy方法

  从例子看出,和上面的一样,这也是个浅拷贝。

>>> import copy
>>> a = {'k1':1,'k2':2,'k3':{'m1':'a','m2':'b'}}
>>> b = copy.deepcopy(a)
>>> a
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> id(a),id(b)
(51561096, 51561160)
>>> a['k1'] = 2
>>> a
{'k1': 2, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> a['k3']['m1'] = 'c'
>>> a
{'k1': 2, 'k3': {'m1': 'c', 'm2': 'b'}, 'k2': 2}
>>> b
{'k1': 1, 'k3': {'m1': 'a', 'm2': 'b'}, 'k2': 2}
>>> id(a['k3']['m1']),id(b['k3']['m1'])
(9787912, 10249080)

deepcopy方法

  从例子中可以看出,deepcopy方法完全复制了一份独立的拷贝。

总结:

1.对于数值和字符串类型,属于轻量级对象,python采用的是‘硬’拷贝的方法,每个变量都成为独立的个体。

2.对于列表和字典,为了节省内存空间,python通常采用浅拷贝的方法,只对最顶层的元素进行复制,而以下层次的内容依然指向同一块地址。只有采用深拷贝的方法才能复制出一份独立的拷贝。列表和字典基本是一样的策略,除了字典没有切片功能。

3.浅拷贝只复制列表和字典的最顶层的元素,对于下级的元素依然指向同样的地址,也就是说有关联性。而深拷贝则是完全复制了一份独立的拷贝。

五、使用建议

1. 对于数值、字符串,直接赋值就好,每一个变量都是独立的个体,不用考虑深浅拷贝的问题。

2.对于列表和字典,如果你想完全复制那么只能用deepcopy()方法;如果浅拷贝就行,那可以用copy()方法。但是,不要赋值,因为这实际没什么意义,只是给变量取了个别名而已。

  

关于python中赋值、浅拷贝、深拷贝之间区别的深入分析的更多相关文章

  1. python中赋值-浅拷贝-深拷贝之间的关系

    赋值: 变量的引用,没有拷贝空间 对象之间赋值本质上 是对象之间的引用传递而已.也就是多个对象指向同一个数据空间. 拷贝的对象分两种类型: . 拷贝可变类型 浅拷贝: 只拷贝第一层数据,不关心里面的第 ...

  2. python中,== 与 is 之间区别

    在python中,== 与 is 之间既有区别,又有联系,本文将通过实际代码的演示,力争能够帮助读到这篇文章的朋友以最短的时间理清二者的关系,并深刻理解它们在内存中的实现机制. 扯淡的话不多说,下面马 ...

  3. python中赋值,深拷贝,浅拷贝区别

    这三种 的区别就是 复制的变量 是否是原变量的引用. 赋值:只是原变量的引用. 浅拷贝和深拷贝的区别 需要通过 子元素 区分 浅拷贝:子元素的 引用相同 深拷贝:所以引用都不相同,完全复制一份 这三种 ...

  4. 深入理解Python中赋值、深拷贝(deepcopy)、浅拷贝(copy)

    赋值 python跟java中的变量本质是不一样的,Python的变量实质上是一个指针(int型或str型),而java的变量是一个可操作的存储空间. a = 123b = a print(id(a) ...

  5. 搞不懂JS中赋值·浅拷贝·深拷贝的请看这里

    前言 百科定义:拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存,然而当内存销毁的时候,指向这 ...

  6. Python中的浅拷贝 深拷贝

    浅拷贝只拷贝父对象,子对象的地址空间不改变,包括下面三种: 1. copy 从下面的例子可以看出对象c从a拷贝,当对象a增加一个列表元素之后,c对象没有改变, 而当对象a中的子列表改变时,对象c的子列 ...

  7. python中赋值和浅拷贝与深拷贝

    初学编程的小伙伴都会对于深浅拷贝的用法有些疑问,今天我们就结合python变量存储的特性从内存的角度来谈一谈赋值和深浅拷贝~~~ 预备知识一——python的变量及其存储 在详细的了解python中赋 ...

  8. 关于python中的is和==的区别

    Python 中的比较:is 与 ==   在 Python 中会用到对象之间比较,可以用 ==,也可以用 is .但是它们的区别是什么呢? is 比较的是两个实例对象是不是完全相同,它们是不是同一个 ...

  9. Python中生成器和迭代器的区别(代码在Python3.5下测试):

    https://blog.csdn.net/u014745194/article/details/70176117 Python中生成器和迭代器的区别(代码在Python3.5下测试):Num01–& ...

随机推荐

  1. 【Android】wifi开发

    WIFI就是一种无线联网技术,常见的是使用无线路由器.那么在这个无线路由器的信号覆盖的范围内都可以采用WIFI连接的方式进行联网.如果无线路由器连接了一个ADSL线路或其他的联网线路,则又被称为“热点 ...

  2. supervisor监控gearman任务

    安装supervisor方法,可以直接用 yum install supervisord ,但是版本可能会旧一点,可以参考官方的方法: easy_install supervisor http://s ...

  3. nginx-upstream-keepalive;accept_mutex-proxy_http_version-1.1-proxy_set_header-connection

    nginx+resin+redis+mysql的架构: 有外部模拟用户请求的大量攻击:它尝试请求了80就断开:看到的现象是在跑有nginx80的主机上TCP连接数少 : ESTABLISHED少: 在 ...

  4. 2015想做O2O?那就来看看O2O报告!

    来源:互联网

  5. mysql自动化安装

    MySQL安装一般使用RPM或者源码安装的方式.RPM安装的优点是快速,方便.缺点是不能自定义安装目录.如果需要调整数据文件和日志文件的存放位置,还需要进行一些手动调整.源码安装的优点是可以自定义安装 ...

  6. VS2013 修改TFS的本地映射路径

    在源代码管理器里面 找到你的本地工作区 然后点击编辑按钮 修改本地目录

  7. CoreImage 处理图片

    1.CoreImage 滤镜的使用(马赛克模糊) CoreImage是苹果公司为了简化图片处理的难度而开发出来的类库. 随着iOS版本号升级以及硬件性能的不断提升,CoreImage将支持越来越多的滤 ...

  8. How to import library ?

    Android Studio: Download or git the library. (for example: the library folder named ActionBarSherloc ...

  9. asp.net发布到IIS中出现错误:处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler”

    asp.net发布到IIS中出现错误:处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler” http:// ...

  10. 太阳升起并下落的小动画-SWIFT

    一个小小的动画,太阳公公上山又下山.先上效果图. 用 lipecap 录的gif效果有点卡顿.好吧,说下如何实现的. 首先在一个大圆内先计算出内切九边形各个顶点的位置,接着连接相应的顶点变成一个九角星 ...