深浅copy

和很多语言一样,Python中也分为简单赋值、浅拷贝、深拷贝这几种“拷贝”方式。

在学习过程中,一开始对浅拷贝理解很模糊。不过经过一系列的实验后,我发现对这三者的概念有了进一步的了解。

一、赋值

赋值算是这三种操作中最常见的了,我们通过一些例子来分析下赋值操作:

str例

  1. >>> a = 'hello'
  2. >>> b = 'hello'
  3. >>> c = a
  4. >>> [id(x) for x in a,b,c]
  5. [4404120000, 4404120000, 4404120000]

由以上指令中,我们可以发现a, b, c三者的地址是一样的。所以以上赋值的操作就相当于c = a = b = 'hello'。

赋值是系统先给一个变量或者对象(这里是'hello')分配了内存,然后再将地址赋给a, b, c。所以它们的地址是相同的。

list例

  1. >>> a = ['hello']
  2. >>> b = ['hello']
  3. >>> c = a
  4. >>> [id(x) for x in a,b,c]
  5. [4403975952, 4404095096, 4403975952]

但是这种情况却不一样了,a和b的地址不同。为何?

因为str是不可变的,所以同样是'hello'只有一个地址,但是list是可变的,所以必须分配两个地址。

这时,我们希望探究以上两种情况如果 修改值 会如何?

str例

  1. >>> a = 'world'
  2. >>> [id(x) for x in a,b,c]
  3. [4404120432, 4404120000, 4404120000]
  4. >>> print a, b, c
  5. world hello hello

这时a的地址和值变了,但是b, c地址和值都未变。因为str的不可变性,a要重新赋值则需重新开辟内存空间,所以a的值改变,a指向的地址改变。b, c由于'hello'的不变性,不会发生改变。

list例

  1. >>> a[0] = 'world'
  2. >>> [id(x) for x in a,b,c]
  3. [4403975952, 4404095096, 4403975952]
  4. >>> print a, b, c
  5. ['world'] ['hello'] ['world']

这时a, c的值和地址均改变,但二者仍相同,b不改变。由于list的可变性,所以修改list的值不需要另外开辟空间,只需修改原地址的值。所以a, c均改变。

在了解了以上的不同点之后,我们就能很好地分析浅拷贝和深拷贝了。

我们均用list作为例子。

二、浅拷贝

  1. >>> a = ['hello', [123, 234]]
  2. >>> b = a[:]
  3. >>> [id(x) for x in a,b]
  4. [4496003656, 4496066752]
  5. >>> [id(x) for x in a]
  6. [4496091584, 4495947536]
  7. >>> [id(x) for x in b]
  8. [4496091584, 4495947536]

Line3,4可以看出a, b地址不同,这符合list是可变的,应开辟不同空间。那浅拷贝就是拷贝了一个副本吗?再看Line5 - 8,我们发现a, b中元素的地址是相同的。如果说字符串'hello'地址一致还能理解,但是第二个元素是list地址仍一致。 这就说明了浅拷贝的特点,只是将容器内的元素的地址复制了一份 。

接着我们尝试修改a, b中的值:

  1. >>> a[0] = 'world'
  2. >>> a[1].append(345)
  3. >>> print 'a = ', a, '\n\r', 'b = ', b
  4. a = ['world', [123, 234, 345]]
  5. b = ['hello', [123, 234, 345]]

a中第一个元素str改变,但是b中未改变;a中第二个元素改变,b中也改变。这就符合不可变的对象修改会开辟新的空间,可变的对象修改不会开辟新空间。也进一步证明了 浅拷贝仅仅是复制了容器中元素的地址 。

三、深拷贝

  1. >>> from copy import deepcopy
  2. >>> a = ['hello', [123, 234]]
  3. >>> b = deepcopy(a)
  4. >>> [id(x) for x in a, b]
  5. [4496066824, 4496066680]
  6. >>> [id(x) for x in a]
  7. [4496091584, 4496067040]
  8. >>> [id(x) for x in b]
  9. [4496091584, 4496371792]

深拷贝后,可以发现a, b地址以及a, b中元素地址均不同。这才是完全 拷贝了一个副本 。

修改a的值后:

  1. >>> a[0] = 'world'
  2. >>> a[1].append(345)
  3. >>> print 'a = ', a, '\n\r', 'b = ', b
  4. a = ['world', [123, 234, 345]]
  5. b = ['hello', [123, 234]]

从Line4,5中可以发现仅仅a修改了,b没有任何修改。 因为b是一个完全的副本,元素地址均与a不同,a修改,b不受影响 。

总结:

1. 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 )。

2. 浅拷贝是在另一块地址中创建一个新的变量或容器,但是容器内的元素的地址均是源对象的元素的地址的拷贝。也就是说新的容器中指向了旧的元素( 新瓶装旧酒 )。

3. 深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是完全的副本。也就是说( 新瓶装新酒 )。

  1. import copy
  2. a = [1, 2, 3, 4, ['a', 'b']] #原始对象
  3.  
  4. b = a #赋值,传对象的引用
  5. c = copy.copy(a) #对象拷贝,浅拷贝
  6. d = copy.deepcopy(a) #对象拷贝,深拷贝
  7.  
  8. a.append(5) #修改对象a
  9. a[4].append('c') #修改对象a中的['a', 'b']数组对象
  10.  
  11. print 'a = ', a
  12. print 'b = ', b
  13. print 'c = ', c
  14. print 'd = ', d
  15.  
  16. 输出结果:
  17. a = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
  18. b = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
  19. c = [1, 2, 3, 4, ['a', 'b', 'c']]
  20. d = [1, 2, 3, 4, ['a', 'b']]

一、数字和字符串

对于 数字 和 字符串 而言,赋值、浅拷贝和深拷贝无意义,因为其永远指向同一个内存地址。

  1. 1 import copy
  2. 2 # ######### 数字、字符串 #########
  3. 3 n1 = 123
  4. 4 # n1 = "i am alex age 10"
  5. 5 print(id(n1))
  6. 6 # ## 赋值 ##
  7. 7 n2 = n1
  8. 8 print(id(n2))
  9. 9 # ## 浅拷贝 ##
  10. 10 n2 = copy.copy(n1)
  11. 11 print(id(n2))
  12. 12
  13. 13 # ## 深拷贝 ##
  14. 14 n3 = copy.deepcopy(n1)
  15. 15 print(id(n3))

二、其他基本数据类型

对于字典、元祖、列表 而言,进行赋值、浅拷贝和深拷贝时,其内存地址的变化是不同的。

1、赋值

赋值,只是创建一个变量,该变量指向原来内存地址,如:

  1. 1 n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
  2. 2
  3. 3 n2 = n1

  

2、浅拷贝

浅拷贝,在内存中只额外创建第一层数据

  1. 1 import copy
  2. 2
  3. 3 n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
  4. 4
  5. 5 n3 = copy.copy(n1) 

3、深拷贝

深拷贝,在内存中将所有的数据重新创建一份(排除最后一层,即:python内部对字符串和数字的优化)

  1. 1 import copy
  2. 2
  3. 3 n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
  4. 4
  5. 5 n4 = copy.deepcopy(n1)

为什么要拷贝?

当进行修改时,想要保留原来的数据和修改后的数据

数字字符串 和 集合 在修改时的差异?(深浅拷贝不同的终极原因)

  1. 1 在修改数据时:
  2. 2 数字字符串:在内存中新建一份数据
  3. 3 集合:修改内存中的同一份数据

对于集合,如何保留其修改前和修改后的数据?

在内存中拷贝一份

对于集合,如何拷贝其n层元素同时拷贝?

深拷贝

  1. 1 copy
  2. 2 >>> dict = {"a":("apple",),"bo":{"b":"banna","o":"orange"},"g":["grape","grapefruit"]}
  3. 3 >>> dict = {"a":("apple",),"bo":{"b":"banna","o":"orange"},"g":["grape","grapefruit"]}
  4. 4 >>> dict2 = dict.copy()
  5. 5
  6. 6
  7. 7 >>> dict["g"][0] = "shuaige" #第一次我修改的是第二层的数据
  8. 8 >>> print dict
  9. 9 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
  10. 10 >>> print dict2
  11. 11 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
  12. 12 >>> id(dict["g"][0]),id(dict2["g"][0])
  13. 13 (140422980581296, 140422980581296) #从这里可以看出第二层他们是用的内存地址
  14. 14 >>>
  15. 15
  16. 16
  17. 17 >>> dict["a"] = "dashuaige" #注意第二次这里修改的是第一层
  18. 18 >>> print dict
  19. 19 {'a': 'dashuaige', 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
  20. 20 >>> print dict2
  21. 21 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
  22. 22 >>>
  23. 23 >>> id(dict["a"]),id(dict2["a"])
  24. 24 (140422980580816, 140422980552272) #从这里看到第一层他们修改后就不会是相同的内存地址了!
  25. 25 >>>
  26. 26
  27. 27
  28. 28 #这里看下,第一次我修改了dict的第二层的数据,dict2也跟着改变了,但是我第二次我修改了dict第一层的数据dict2没有修改。
  29. 29 说明:浅copy只是第一层是独立的,其他层面是公用的!作用节省内存
  30. 30
  31. 31 copy
  32. 32
  33. 33 >>> import copy #深copy需要导入模块
  34. 34 >>> dict = {"a":("apple",),"bo":{"b":"banna","o":"orange"},"g":["grape","grapefruit"]}
  35. 35 >>> dict2 = copy.deepcopy(dict)
  36. 36 >>> print dict
  37. 37 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['grape', 'grapefruit']}
  38. 38 >>> print dict2
  39. 39 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['grape', 'grapefruit']}
  40. 40 >>> dict["g"][0] = "shuaige" #修改第二层数据
  41. 41 >>> print dict
  42. 42 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
  43. 43 >>> print dict2
  44. 44 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['grape', 'grapefruit']}
  45. 45 >>> id(dict["g"][0]),id(dict2["g"][0])
  46. 46 (140422980580816, 140422980580288) #从这里看到第二个数据现在也不是公用了
  47. 47
  48. 48 # 通过这里可以看出他们现在是一个完全独立的,当你修改dict时dict2是不会改变的因为是两个独立的字典!

python基础知识5——赋值与深浅拷贝——整数和字符串,列表元组字典的更多相关文章

  1. Python第三天 序列 数据类型 数值 字符串 列表 元组 字典

    Python第三天 序列  数据类型  数值  字符串  列表  元组  字典 数据类型数值字符串列表元组字典 序列序列:字符串.列表.元组序列的两个主要特点是索引操作符和切片操作符- 索引操作符让我 ...

  2. Python第三天 序列 5种数据类型 数值 字符串 列表 元组 字典 各种数据类型的的xx重写xx表达式

    Python第三天 序列  5种数据类型  数值  字符串  列表  元组  字典 各种数据类型的的xx重写xx表达式 目录 Pycharm使用技巧(转载) Python第一天  安装  shell ...

  3. 【python】变量的赋值、深浅拷贝

    python——赋值与深浅拷贝 https://www.cnblogs.com/Eva-J/p/5534037.html 啥都不说,看这个博主的文章!

  4. python基础知识-7-内存、深浅、文件操作

    python其他知识目录 1.一些对内存深入理解的案例 以下列举列表,列表/字典/集合这些可变类型都是一样的原理 变量是个地址,指向存储数据的内存空间的地址,它的实质就相当于c语言里的指针.变量和数据 ...

  5. python基础-3 集合 三元运算 深浅拷贝 函数 Python作用域

    上节课总结 1 运算符 in 字符串 判断  : “hello” in "asdasfhelloasdfsadf" 列表元素判断:"li" in ['li', ...

  6. Python自动化开发 - 字符串, 列表, 元组, 字典和和文件操作

    一.字符串 特性:字符串本身不可修改,除非字符串变量重新赋值.Python3中所有字符串都是Unicode字符串,支持中文. >>> name  = "Jonathan&q ...

  7. python字符串/列表/元组/字典之间的相互转换(5)

    一.字符串str与列表list 1.字符串转列表 字符串转为列表list,可以使用str.split()方法,split方法是在字符串中对指定字符进行切片,并返回一个列表,示例代码如下: # !usr ...

  8. Python 整数 长整数 浮点数 字符串 列表 元组 字典的各种方法

    对于Python, 一切事物都是对象,对象基于类创建!! 注:查看对象相关成员var,type, dir 一.整数 如: 18.73.84 每一个整数都具备如下需要知道的功能: def bit_len ...

  9. Python笔记【5】_字符串&列表&元组&字典之间转换学习

    #!/usr/bin/env/python #-*-coding:utf-8-*- #Author:LingChongShi #查看源码Ctrl+左键 #数据类型之间的转换 Str='www.baid ...

随机推荐

  1. 基于服务(Web Service)的文件管理Winform程序实现

    1.       描述 面向服务的体系结构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来.接口是采用中立的方式进行定义的,它应该独立于实现 ...

  2. java容器的一些存取用法

    关于集合(list,set,map)的遍历与增强for循环的使用     java集合类的使用可以说是无处不在,总的我们可以将之分为三大块,分别是从Collection接口延伸出的List.Set和以 ...

  3. AWS CloudFormation Template

    { "AWSTemplateFormatVersion" : "2010-09-09", "Parameters" : { "Ba ...

  4. Building GCC 4.1.2 in CentOS 7 for Maya API development

    Following the official guid: http://help.autodesk.com/cloudhelp/2015/ENU/Maya-SDK/files/Setting_up_y ...

  5. expr命令的一些用法

    expr是evaluate expressions的缩写,我的理解它的作用就是用来输出表达式的值. 看下面的几个例子. (1)进行数值运算 $:expr 1 + 2     //'+' 左右两边必须有 ...

  6. 在Eclipse中使用JUnit4进行单元测试(中级篇)

    我们继续对初级篇中的例子进行分析.初级篇中我们使用Eclipse自动生成了一个测试框架,在这篇文章中,我们来仔细分析一下这个测试框架中的每一个细节,知其然更要知其所以然,才能更加熟练地应用JUnit4 ...

  7. windows和linux实现文件共享

    linux和windows实现共享,需要安装samba服务器 安装步骤: 1.查看是否已经安装samba rpm -q samba 2.如果已经安装,如果你想再次安装,可以卸载 rpm -e samb ...

  8. vs2010下C++调用lib或dll文件

    注: DLL:表示链接库,包含dll,lib文件: dll: 表示my.dll文件 lib: 表示my.lib文件 C++ 调用.lib的方法: 一: 隐式的加载时链接,有三种方法 1  设置工程的 ...

  9. win7(64位)php5.5-Apache2.4-mysql5.6环境安装

    原文链接http://jingyan.baidu.com/article/9faa723152c5d6473d28cb47.html 工具/原料 php-5.5.10-Win32-VC11-x64.z ...

  10. 检测文件是否有BOM 头,并删除BOM头(php)

    将下面文件放在网站根目录访问即可,它会遍历当前目录下所有子目录,检测文件是否含有BOM头,并删除BOM头 <?php //remove the utf-8 boms //by magicbug ...