首先查看拷贝模块(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. kubeadm 搭建 K8S集群

    kubeadm是K8s官方推荐的快速搭建K8s集群的方法. 环境: Ubuntu 16.04 1 安装docker Install Docker from Ubuntu’s repositories: ...

  2. 数据库:mysql 获取刚插入行id[转]

    我们在写数据库程序的时候,经常会需要获取某个表中的最大序号数, 一般情况下获取刚插入的数据的id,使用select max(id) from table 是可以的.但在多线程情况下,就不行了. 下面介 ...

  3. TS学习之解构与展开

    一.解构 1.解构数组 let input = [1, 2]; let [first, second] = input; console.log(first); // outputs 1 consol ...

  4. spring+jax 出现java.io.Serializable is an interface, and JAXB can't handle interfaces

    spring+jax 出现java.io.Serializable is an interface, and JAXB can't handle interfaces 原因是我的webservice方 ...

  5. jQuery菜单,导航与标签页

    一:导航 网站导航栏是网站导航的第一层导航结构,我们可以使用HTML和jQuery来创建类似flash的动画效果 一:下拉式菜单  法一: <!DOCTYPE html PUBLIC " ...

  6. R: 关于 table 函数的应用

    ################################################### 问题:关于 table 函数   18.5.9 来一个关于 table 函数的例子,说明tabl ...

  7. Entity Framework Code-First(9.11):DataAnnotations - InverseProperty Attribute

    DataAnnotations - InverseProperty Attribute: We have seen in the Code-First Convention section that ...

  8. Struts2学习第七课 通配符映射

    一个WEB应用可能有长百上千个action声明,可以利用struts提供的通配符映射机制吧多个彼此相识的映射关系简化为一个映射关系. 通配符映射规则: --若找到多个匹配,没有通配符的那个将胜出(精确 ...

  9. Android Market google play store帐号注册方法流程 及发布应用注意事项

    Android Market google play store帐号申请 注册方法流程 在 Google Play 中发布软件之前,您需要完成以下三项工作: 创建开发人员个人资料 接受开发人员分发协议 ...

  10. DES加密解决算法

    /// <summary> /// DES加密算法 /// sKey为8位或16位 /// </summary> /// <param name="pToEnc ...