首先查看拷贝模块(copy)发现:

>>> help(copy)
Help on module copy:
NAME
    copy - Generic (shallow and deep) copying operations.
DESCRIPTION
    Interface summary:   
            import copy   
            x = copy.copy(y)        # make a shallow copy of y
            x = copy.deepcopy(y)    # make a deep copy of y   
    For module specific errors, copy.Error is raised.   
    The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or
    class instances).
    
    - A shallow copy constructs a new compound object and then (to the extent possible) inserts *the same objects* into it that the
      original contains.
    
    - A deep copy constructs a new compound object and then, recursively,  inserts *copies* into it of the objects found in the original.

...(here omitted 10000words)

由以上的信息可知:

1、相同点:都拷贝出了一个新的复合对象;

2、不同点:浅拷贝—— 在拷贝出的新的对象上插入(引用)源list对象的一切;

深拷贝—— 递归地拷贝源list对象中的一切。(彻头彻尾的另立门户)

现在出现了一个新的问题—— 拷贝

在计算机中拷贝一份数据或者拷贝一个变量,意味着系统需分配新的内存用于对拷贝数据的存放。

我们先来讨论一下变量的赋值(变量的数据结构中的内存地址域的拷贝)过程。

首先看一下变量的赋值过程:

 Python 2.6.6 (r266:84292, Aug 18 2016, 15:13:37)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 3
>>> b = a
>>> id(a)
7488264
>>> id(b)
7488264
>>> a = 4
>>> id(a)
7488240
>>> id(b) # 咦,b没有随a发生改变
7488264
>>> b
3

要解释这个,必须要了解变量的数据结构。

当向系统申请创建一个变量时,系统先分配一块内存空间,该内存空间用于存储该变量。

变量的数据结构包括2部分:第一部分用于存储变量的名称和变量的数据类型的长度,第二部分用于存储内存地址(即索引)。

当变量未初始化时,第二部分数据为垃圾值;一旦初始化,该部分的值即为初始化值的内存地址。

例如:以上 a = 3, 其过程如下:

首先系统为常量3(int型)分配一块内存大小为4byte的空间存放常量3;然后将常量3的内存地址存储于变量a的第二部分。这样就完成了变量a的赋值过程。

b = a时,同样系统先分配一块内存空间存放变量b, 之后系统将a中的第二部分数据拷贝到b中的第二部分。

而id()的返回值正是变量的第二部分数据(内存地址)。

所以当执行a时,是根据第二部分的数据(内存地址)获取该内存的值。

当a = 4 时,变量a第二部分的数据即为常量4的存储地址,因此id(a)发生改变,而id(b)保持不变。

如下图:

回到浅拷贝和深拷贝的议题:

浅拷贝—— 在拷贝出的新的对象上插入(引用)源list对象的一切;

深拷贝—— 递归地拷贝源list对象中的一切。(彻头彻尾的另立门户)。

浅拷贝的实例:

 #!/usr/bin/python                  # Python2
#
import copy will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.copy(will) print id(will) #
print will # ['Will', 28, ['Python', 'C#', 'JavaScript']]
print [id(ele) for ele in will] # [140337318374208, 13394096, 140337318282160]
print '============================'
print id(will[2]) #
print id(will[2][0]) #
print id(wilber[2][0]) #
print id(wilber) #
print wilber # ['Will', 28, ['Python', 'C#', 'JavaScript']]
print [id(ele) for ele in wilber] # [140337318374208, 13394096, 140337318282160] will[0] = "Wilber"
will[2].append("CSS")
print id(will) #
print will # ['Wilber', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
print [id(ele) for ele in will] # [140337318374448, 13394096, 140337318282160]
print id(wilber) #
print wilber # ['Will', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
print [id(ele) for ele in wilber] # [140337318374208, 13394096, 140337318282160]

浅拷贝只是生成一个新的对象,数据结构以及索引关系未变。

浅拷贝时,列表will与wilber由系统分配不同的地址,系统将列表will的第一层进行拷贝即:will[0], will[1], will[2]拷贝,故wilber[0]与will[0],wilber[1]与will[1],wilber[2]与will[2],指向相同的内存地址。

如下图所示:

深拷贝实例:

 #!/usr/bin/python
#
import copy will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.deepcopy(will) print id(will) # 139899797283040
print will # ['Will', 28, ['Python', 'C#', 'JavaScript']]
print [id(ele) for ele in will] # [139899797338992, 11432112, 139899797246896]
print '============='
print id(will[2]) #
print id(wilber[2]) #
print id(will[2][0]) #
print id(wilber[2][0]) #
print id(wilber[2][1]) #
print id(wilber) #
print wilber # ['Will', 28, ['Python', 'C#', 'JavaScript']]
print [id(ele) for ele in wilber] # [139899797338992, 11432112, 139899797351024] will[0] = "Wilber"
will[2].append("CSS")
print id(will) #
print will # ['Wilber', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
print [id(ele) for ele in will] # [139899797339280, 11432112, 139899797246896]
print id(wilber) #
print wilber # ['Will', 28, ['Python', 'C#', 'JavaScript']]
print [id(ele) for ele in wilber] # [139899797338992, 11432112, 139899797351024]

深拷贝会递归(逐层)拷贝list的数据结构。

深拷贝时,系统将列表will逐层进行拷贝即:列表will与wilbe,will[2]与wilber[2]由系统分配不同的地址,will[0], will[1], will[2],will[2][0], will[2][1], will[2][2]拷贝;

故wilber[0]与will[0],wilber[1]与will[1], will[2][0]与wilber[2][0], will[2][1]与wilber[2][0], will[2][2]与wilber[2][2],指向相同的内存地址。

附注-list之间的赋值代码:

 #!/usr/bin/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]

由Python的浅拷贝(shallow copy)和深拷贝(deep copy)引发的思考的更多相关文章

  1. 浅拷贝(Shallow Copy) VS 深拷贝(Deep Copy)

    首先,深拷贝和浅拷贝针对的是对象类型(对象,数组,函数) 浅拷贝指的是只是拷贝了对象的引用地址,彼此之间高耦合,一个改变,另一个可能也随之改变: 深拷贝是指只是完整的将变量的值拷贝过来,是一个新的对象 ...

  2. [置顶] operator overloading(操作符重载,运算符重载)运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy)

    operator overloading(操作符重载,运算符重载) 所谓重载就是重新赋予新的意义,之前我们已经学过函数重载,函数重载的要求是函数名相同,函数的参数列表不同(个数或者参数类型).操作符重 ...

  3. copy&mutableCopy 浅拷贝(shallow copy)深拷贝 (deep copy)

    写在前面 其实看了这么多,总结一个结论: 拷贝的初衷的目的就是为了:修改原来的对象不能影响到拷贝出来得对象 && 修改拷贝出来的对象也不能影响到原来的对象 所以,如果原来对象就是imm ...

  4. angular.extend深拷贝(deep copy)

    在用到angular.extend的时候,正好碰到一个对象,是层层嵌套的Array, 结果发现只能extend第一层,查阅官文档,确实不支持deep copy: Note: Keep in mind ...

  5. [置顶] 运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy),三大件(bigthree problem)

    一般的我们喜欢这样对对象赋值: Person p1;Person p2=p1; classT object(another_object), or    A a(b); classT object = ...

  6. 运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy),三大件(bigthree problem)

    一般的我们喜欢这样对对象赋值: Person p1;Person p2=p1; classT object(another_object), or    A a(b); classT object = ...

  7. javascript 求最大前5个数; 对象 深拷贝 deep copy

    * 用数组 function getTopN(a, n) { function _cloneArray(aa) { var n = aa.length, a = new Array(n); for ( ...

  8. Python 列表浅拷贝与深拷贝

    浅拷贝 shallow copy 和深拷贝 deep copy list.copy() 浅拷贝:复制此列表(只复制一层,不会复制深层对象) 等同于 L[:] 举例: 浅拷贝: a = [1.1, 2. ...

  9. python deep copy and shallow copy

    Python中对于对象的赋值都是引用,而不是拷贝对象(Assignment statements in Python do not copy objects, they create bindings ...

随机推荐

  1. css关于内外边距的详细解释

    贴图吧,图一眼明了. 无效果时候有 只有一个<div>啦啦啦</div> 只有padding时候有: padding详细设计时: 解释:padding是对内的,如padding ...

  2. 通信端口Com口被占用的原因分析

    目前在调试地磅读取程序,近一段时间无法读取,排查原因发现是com1端口被占用. 从网上找了无数个文章,最终得到一条有价值的消息, 原因如下: com1端口不能读取电子地磅的数据了,重启之后发现 有一个 ...

  3. vue.js 使用高德地图

    1.获取key值 注册成为高德开发者需要分三步: 第一步,注册高德开发者: 第二步,去控制台创建应用: 第三步,获取Key 2.修改配置文件  webpack.base.conf.js externa ...

  4. day03 hadoop的解压与配置文件的配置,还需要看视频

    拷贝hadoop1.2.1.tar.gz安装包到其他的节点上 scp -r ~/hadoop-1.2.1.tar.gz  root@node2:~/  tar -zxvf hadoop-1.2.1.t ...

  5. C++中对象的常引用

    直接传递对象名 用对象名做函数参数时,在函数调用时将建立一个新的对象,它是形参对象的拷贝. ================下面给出一个直接传递对象名的例子程序1.1================= ...

  6. Jmeter分布式测试需要注意事项

    Jmeter分布式测试需要注意事项: 1. 如果脚本中有用到CSV Data Set Config,则所有的模拟机都必须在相应的目录下存在该文件.如下图,则必须所有模拟机的F盘下都有user.txt文 ...

  7. 10、Perl5中19个最重要的文件系统工具

    转载:http://www.cnblogs.com/nkwy2012/p/6027157.html 在写脚本处理文件系统时,经常需要加载很多模块.其中好多有用函数分散在各种不同的模块中.它们有些是Pe ...

  8. Entity Framework Code-First(12):Configure One-to-Many

    Configure One-to-Many Relationship: Here, we will learn how to configure One-to-Many relationship be ...

  9. Entity Framework Code-First(10.1):EntityTypeConfiguration

    EntityTypeConfiguration Class in Code-First: Before we start to configure using Fluent API, let's se ...

  10. 【Qt官方例程学习笔记】Raster Window Example(画笔的平移/旋转/缩放应用)

    这个例子显示了如何使用QPainter渲染一个简单的QWindow. 值得学习的内容 <QtGui>头文件 #include <QtGui>就可以使用Qt GUI模块中的所有类 ...