首先查看拷贝模块(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. linux python 更新版本

    更新python: 第1步:更新gcc,因为gcc版本太老会导致新版本python包编译不成功 代码如下: #yum -y install gcc 系统会自动下载并安装或更新,等它自己结束 第2步:下 ...

  2. C#如何对DataTable中的数据进行条件搜索

    经常遇到将数据库中的数据读取到DataTable中的时候再次对DataTable进行条件筛选,下面的筛选的一个例子: DataRow[] dr = dt.Select("token = '& ...

  3. Spring Boot中使用RabbitMQ

    很久没有写Spring Boot的内容了,正好最近在写Spring Cloud Bus的内容,因为内容会有一些相关性,所以先补一篇关于AMQP的整合. Message Broker与AMQP简介 Me ...

  4. MyBatis总结(1)

    MyBatis前身是ibatis,是一个数据持久层框架.封装优化了普通JDBC过程, 如数据库连接的创建.设置SQL语句参数.执行SQL语句.事务.结果映射以及资源释放等. MyBatis是一个支持普 ...

  5. eclipse安装WTP部署WEB项目

    打开WTP官方安装指南,找到想要的下载站点 http://wiki.eclipse.org/WTP_FAQ#How_do_I_install_WTP.3F 我选择的是http://download.e ...

  6. 【转】经典网文:追MM与设计模式

    设计模式做为程序员的“内功心法”,越来越受到.net 社区的重视,这种变化是很可喜的,Java社区走在了我们的前面,但这种状况也许有一天会发生改变. 从追MM谈Java的23种设计模式1.FACT ...

  7. 泛型中new()约束的用法

    一..NET中支持的类型参数约束有以下几种 where T : struct              T必须是一个结构类型where T : class               T必须是一个类( ...

  8. 20. Linux提权:从入门到放弃

    几点前提 已经拿到低权shell 被入侵的机器上面有nc,python,perl等linux非常常见的工具 有权限上传文件和下载文件 内核漏洞提权 提到脏牛,运维流下两行眼泪,我们留下两行鼻血.内核漏 ...

  9. MySQL 文件导入出错

    ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot exec ...

  10. python sys.sdout.write 和print 区别

    sys.sdout.write 标准输入相当于“%value%”,输出内容没有空格,而print输出带有空格,举个例子 用sys.sdout.write: import sys for i in ra ...