python基础知识5——赋值与深浅拷贝——整数和字符串,列表元组字典
深浅copy
和很多语言一样,Python中也分为简单赋值、浅拷贝、深拷贝这几种“拷贝”方式。
在学习过程中,一开始对浅拷贝理解很模糊。不过经过一系列的实验后,我发现对这三者的概念有了进一步的了解。
一、赋值
赋值算是这三种操作中最常见的了,我们通过一些例子来分析下赋值操作:
str例
>>> a = 'hello'
>>> b = 'hello'
>>> c = a
>>> [id(x) for x in a,b,c]
[4404120000, 4404120000, 4404120000]
由以上指令中,我们可以发现a, b, c三者的地址是一样的。所以以上赋值的操作就相当于c = a = b = 'hello'。
赋值是系统先给一个变量或者对象(这里是'hello')分配了内存,然后再将地址赋给a, b, c。所以它们的地址是相同的。
list例
>>> a = ['hello']
>>> b = ['hello']
>>> c = a
>>> [id(x) for x in a,b,c]
[4403975952, 4404095096, 4403975952]
但是这种情况却不一样了,a和b的地址不同。为何?
因为str是不可变的,所以同样是'hello'只有一个地址,但是list是可变的,所以必须分配两个地址。
这时,我们希望探究以上两种情况如果 修改值 会如何?
str例
>>> a = 'world'
>>> [id(x) for x in a,b,c]
[4404120432, 4404120000, 4404120000]
>>> print a, b, c
world hello hello
这时a的地址和值变了,但是b, c地址和值都未变。因为str的不可变性,a要重新赋值则需重新开辟内存空间,所以a的值改变,a指向的地址改变。b, c由于'hello'的不变性,不会发生改变。
list例
>>> a[0] = 'world'
>>> [id(x) for x in a,b,c]
[4403975952, 4404095096, 4403975952]
>>> print a, b, c
['world'] ['hello'] ['world']
这时a, c的值和地址均改变,但二者仍相同,b不改变。由于list的可变性,所以修改list的值不需要另外开辟空间,只需修改原地址的值。所以a, c均改变。
在了解了以上的不同点之后,我们就能很好地分析浅拷贝和深拷贝了。
我们均用list作为例子。
二、浅拷贝
>>> a = ['hello', [123, 234]]
>>> b = a[:]
>>> [id(x) for x in a,b]
[4496003656, 4496066752]
>>> [id(x) for x in a]
[4496091584, 4495947536]
>>> [id(x) for x in b]
[4496091584, 4495947536]
Line3,4可以看出a, b地址不同,这符合list是可变的,应开辟不同空间。那浅拷贝就是拷贝了一个副本吗?再看Line5 - 8,我们发现a, b中元素的地址是相同的。如果说字符串'hello'地址一致还能理解,但是第二个元素是list地址仍一致。 这就说明了浅拷贝的特点,只是将容器内的元素的地址复制了一份 。
接着我们尝试修改a, b中的值:
>>> a[0] = 'world'
>>> a[1].append(345)
>>> print 'a = ', a, '\n\r', 'b = ', b
a = ['world', [123, 234, 345]]
b = ['hello', [123, 234, 345]]
a中第一个元素str改变,但是b中未改变;a中第二个元素改变,b中也改变。这就符合不可变的对象修改会开辟新的空间,可变的对象修改不会开辟新空间。也进一步证明了 浅拷贝仅仅是复制了容器中元素的地址 。
三、深拷贝
>>> from copy import deepcopy
>>> a = ['hello', [123, 234]]
>>> b = deepcopy(a)
>>> [id(x) for x in a, b]
[4496066824, 4496066680]
>>> [id(x) for x in a]
[4496091584, 4496067040]
>>> [id(x) for x in b]
[4496091584, 4496371792]
深拷贝后,可以发现a, b地址以及a, b中元素地址均不同。这才是完全 拷贝了一个副本 。
修改a的值后:
>>> a[0] = 'world'
>>> a[1].append(345)
>>> print 'a = ', a, '\n\r', 'b = ', b
a = ['world', [123, 234, 345]]
b = ['hello', [123, 234]]
从Line4,5中可以发现仅仅a修改了,b没有任何修改。 因为b是一个完全的副本,元素地址均与a不同,a修改,b不受影响 。
总结:
1. 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 )。
2. 浅拷贝是在另一块地址中创建一个新的变量或容器,但是容器内的元素的地址均是源对象的元素的地址的拷贝。也就是说新的容器中指向了旧的元素( 新瓶装旧酒 )。
3. 深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是完全的副本。也就是说( 新瓶装新酒 )。
import copy
a = [1, 2, 3, 4, ['a', 'b']] #原始对象 b = a #赋值,传对象的引用
c = copy.copy(a) #对象拷贝,浅拷贝
d = copy.deepcopy(a) #对象拷贝,深拷贝 a.append(5) #修改对象a
a[4].append('c') #修改对象a中的['a', 'b']数组对象 print 'a = ', a
print 'b = ', b
print 'c = ', c
print 'd = ', d 输出结果:
a = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
b = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
c = [1, 2, 3, 4, ['a', 'b', 'c']]
d = [1, 2, 3, 4, ['a', 'b']]
一、数字和字符串
对于 数字 和 字符串 而言,赋值、浅拷贝和深拷贝无意义,因为其永远指向同一个内存地址。
1 import copy
2 # ######### 数字、字符串 #########
3 n1 = 123
4 # n1 = "i am alex age 10"
5 print(id(n1))
6 # ## 赋值 ##
7 n2 = n1
8 print(id(n2))
9 # ## 浅拷贝 ##
10 n2 = copy.copy(n1)
11 print(id(n2))
12
13 # ## 深拷贝 ##
14 n3 = copy.deepcopy(n1)
15 print(id(n3))
二、其他基本数据类型
对于字典、元祖、列表 而言,进行赋值、浅拷贝和深拷贝时,其内存地址的变化是不同的。
1、赋值
赋值,只是创建一个变量,该变量指向原来内存地址,如:
1 n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
2
3 n2 = n1
2、浅拷贝
浅拷贝,在内存中只额外创建第一层数据
1 import copy
2
3 n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
4
5 n3 = copy.copy(n1)
3、深拷贝
深拷贝,在内存中将所有的数据重新创建一份(排除最后一层,即:python内部对字符串和数字的优化)
1 import copy
2
3 n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
4
5 n4 = copy.deepcopy(n1)
为什么要拷贝?
当进行修改时,想要保留原来的数据和修改后的数据
数字字符串 和 集合 在修改时的差异?(深浅拷贝不同的终极原因)
1 在修改数据时:
2 数字字符串:在内存中新建一份数据
3 集合:修改内存中的同一份数据
对于集合,如何保留其修改前和修改后的数据?
在内存中拷贝一份
对于集合,如何拷贝其n层元素同时拷贝?
深拷贝
1 浅copy
2 >>> dict = {"a":("apple",),"bo":{"b":"banna","o":"orange"},"g":["grape","grapefruit"]}
3 >>> dict = {"a":("apple",),"bo":{"b":"banna","o":"orange"},"g":["grape","grapefruit"]}
4 >>> dict2 = dict.copy()
5
6
7 >>> dict["g"][0] = "shuaige" #第一次我修改的是第二层的数据
8 >>> print dict
9 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
10 >>> print dict2
11 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
12 >>> id(dict["g"][0]),id(dict2["g"][0])
13 (140422980581296, 140422980581296) #从这里可以看出第二层他们是用的内存地址
14 >>>
15
16
17 >>> dict["a"] = "dashuaige" #注意第二次这里修改的是第一层
18 >>> print dict
19 {'a': 'dashuaige', 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
20 >>> print dict2
21 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
22 >>>
23 >>> id(dict["a"]),id(dict2["a"])
24 (140422980580816, 140422980552272) #从这里看到第一层他们修改后就不会是相同的内存地址了!
25 >>>
26
27
28 #这里看下,第一次我修改了dict的第二层的数据,dict2也跟着改变了,但是我第二次我修改了dict第一层的数据dict2没有修改。
29 说明:浅copy只是第一层是独立的,其他层面是公用的!作用节省内存
30
31 深copy
32
33 >>> import copy #深copy需要导入模块
34 >>> dict = {"a":("apple",),"bo":{"b":"banna","o":"orange"},"g":["grape","grapefruit"]}
35 >>> dict2 = copy.deepcopy(dict)
36 >>> print dict
37 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['grape', 'grapefruit']}
38 >>> print dict2
39 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['grape', 'grapefruit']}
40 >>> dict["g"][0] = "shuaige" #修改第二层数据
41 >>> print dict
42 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['shuaige', 'grapefruit']}
43 >>> print dict2
44 {'a': ('apple',), 'bo': {'b': 'banna', 'o': 'orange'}, 'g': ['grape', 'grapefruit']}
45 >>> id(dict["g"][0]),id(dict2["g"][0])
46 (140422980580816, 140422980580288) #从这里看到第二个数据现在也不是公用了
47
48 # 通过这里可以看出他们现在是一个完全独立的,当你修改dict时dict2是不会改变的因为是两个独立的字典!
python基础知识5——赋值与深浅拷贝——整数和字符串,列表元组字典的更多相关文章
- Python第三天 序列 数据类型 数值 字符串 列表 元组 字典
Python第三天 序列 数据类型 数值 字符串 列表 元组 字典 数据类型数值字符串列表元组字典 序列序列:字符串.列表.元组序列的两个主要特点是索引操作符和切片操作符- 索引操作符让我 ...
- Python第三天 序列 5种数据类型 数值 字符串 列表 元组 字典 各种数据类型的的xx重写xx表达式
Python第三天 序列 5种数据类型 数值 字符串 列表 元组 字典 各种数据类型的的xx重写xx表达式 目录 Pycharm使用技巧(转载) Python第一天 安装 shell ...
- 【python】变量的赋值、深浅拷贝
python——赋值与深浅拷贝 https://www.cnblogs.com/Eva-J/p/5534037.html 啥都不说,看这个博主的文章!
- python基础知识-7-内存、深浅、文件操作
python其他知识目录 1.一些对内存深入理解的案例 以下列举列表,列表/字典/集合这些可变类型都是一样的原理 变量是个地址,指向存储数据的内存空间的地址,它的实质就相当于c语言里的指针.变量和数据 ...
- python基础-3 集合 三元运算 深浅拷贝 函数 Python作用域
上节课总结 1 运算符 in 字符串 判断 : “hello” in "asdasfhelloasdfsadf" 列表元素判断:"li" in ['li', ...
- Python自动化开发 - 字符串, 列表, 元组, 字典和和文件操作
一.字符串 特性:字符串本身不可修改,除非字符串变量重新赋值.Python3中所有字符串都是Unicode字符串,支持中文. >>> name = "Jonathan&q ...
- python字符串/列表/元组/字典之间的相互转换(5)
一.字符串str与列表list 1.字符串转列表 字符串转为列表list,可以使用str.split()方法,split方法是在字符串中对指定字符进行切片,并返回一个列表,示例代码如下: # !usr ...
- Python 整数 长整数 浮点数 字符串 列表 元组 字典的各种方法
对于Python, 一切事物都是对象,对象基于类创建!! 注:查看对象相关成员var,type, dir 一.整数 如: 18.73.84 每一个整数都具备如下需要知道的功能: def bit_len ...
- Python笔记【5】_字符串&列表&元组&字典之间转换学习
#!/usr/bin/env/python #-*-coding:utf-8-*- #Author:LingChongShi #查看源码Ctrl+左键 #数据类型之间的转换 Str='www.baid ...
随机推荐
- pjax技术的应用
一.什么是PJAX? 现在有一些网站(apicloud, github)支持这样一种浏览方式,当你点击站内的一个连接的时候,不是传统的跳转到另外一个连接,而是类似ajax的局部刷新改变页面内容,但是 ...
- JavaServer Faces 2.0 can not be installed解决方案
问题描述:maven项目出现如下错误 JavaServer Faces 2.0 requires Dynamic Web Module 2.5 or newer..Maven Java EE Conf ...
- 在Linux上编写C#程序
自从C#开源之后,在Linux编写C#程序就成了可能.Mono-project就是开源版本的C#维护项目.在Linux平台上使用的C#开发工具为monodevelop.安装方式如下: 首先需要安装一些 ...
- javascript属性标签
- 无需Try catch 的UI事件封装类
在UI处理中,经常需要进行异常处理,以便在错误发生时能够进行一些自定义的操作,比如,弹出消息框给用户,进行重试操作,记录日志等,如果能够让用户写代码时不用写try...catch,而只是关注业务逻辑的 ...
- c++容器
1.vector:实质是动态堆数组,连续存储的内存区域,支持快速的随机访问. 2.list:实质是双向循环链表,支持在中间进行快速的插入删除,但是不能支持快速的随机访问.非连续的内存区域. 3.deq ...
- C语言fmod()函数:对浮点数取模(求余)
头文件:#include <math.h> fmod() 用来对浮点数进行取模(求余),其原型为: double fmod (double x); 设返回值为 ret,那么 x = ...
- 新增资产时YTD折旧与累计折旧录入错误如何处理
如新增资产时YTD折旧与累计折旧录入错误,但资产已入账处理,如何处理: 1.需要先报废资产: 2.需要在总账手工帐冲销未冲抵凭证: 3.重新增加资产,录入资产时YTD折旧及累计折旧金额应为0.
- Nopcommerce 二次开发1 基础
1 Doamin 酒店 namespace Nop.Core.Domain.Hotels { /// <summary> /// 酒店 /// </summary> p ...
- 剑指Offer:解决难题时的三大方法
1.画图 让抽象的东西变得直观生动起来.比如设计二叉树,链表,栈,队列这些数据结构时. 2.举例子 同样可以化抽象为直观.能够更清晰的展现思路.从例子归纳出一般做法. 3.分解 有时问题本身是比较复杂 ...