首先查看拷贝模块(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. 【转】 Pro Android学习笔记(六十):Preferences(4):MultiSelect List Preference

    目录(?)[-] XML文件 在设备中保存 读出信息 ListPreference提供单选列表,我们可以通过CheckBoxPreference提供多选列表.此外,Android在3.0后提供Mult ...

  2. js中this

    首先声明,我是小白,以下只是自己的简单理解. 先看下面的代码: (function () { console.log(this); })(); 毫无疑虑,输出的是window. 在看下面代码: (fu ...

  3. mysql--事务demo1----

    package com.etc.entity; import java.sql.Connection; import java.sql.PreparedStatement; import java.s ...

  4. Centos7 忘记密码的情况下,修改root或其他用户密码

    转载:https://blog.csdn.net/wcy00q/article/details/70570043 应用场景 linux管理员忘记root密码,需要进行找回操作. 注意事项:本文基于ce ...

  5. Windchill 配置LOG文件,使开发中的代码能显示打印的信息

    如开发代码的类HomeLogic.java, 包路径在pnt.report.home 需求:需监控此类的打印数据 方法:配置D:\ptc\Windchill_10.1\Windchill\codeba ...

  6. 菜鸟级的Git与GitHub使用总结(转)

    菜鸟级的Git与GitHub使用总结 原创 2016年12月01日 14:58:30 1792 前言 这几天一直在折腾学习Git和GitHub的使用.几天下来,在网上查阅了大量的资料,总算有一些成果. ...

  7. Learning Python 012 函数式编程 2 返回函数 匿名函数 装饰器 偏函数

    Python 函数式编程 2 返回函数 返回函数的意思就是:函数作为返回值.(高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回.) 举个例子:实现一个可变参数的求和. 正常的函数: de ...

  8. 应用程序无法正常启动提示错误0xc000007b 问题的原因和解决方法

    应用程序无法正常启动提示错误0xc000007b 问题的原因和解决方法 前提条件: 你使用的是VS201x软件编写程序,你使用的电脑是X64位的,并且你在使用OpenCV库.你编写的程序可以正常编译, ...

  9. 转:基于InfluxDB&Grafana的JMeter实时性能测试数据的监控和展示

    本文主要讲述如何利用JMeter监听器Backend Listener,配合使用InfluxDB+Grafana展示实时性能测试数据 关于JMeter实时测试数据 JMeter从2.11版本开始,命令 ...

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

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