Python变量存储方式以及深浅拷贝
(关于学习廖雪峰Python教程的一些个人总结)
变量存储
Python是动态语言。
可以把任意数据类型赋值给变量,无需提前声明变量类型,同一个变量可以反复赋值,而且可以是不同类型的变量。
下面引用廖雪峰Python教程的简单例子,描述变量存储过程:
当我们写
a = 'ABC'
时,Python解释器干了两件事情:
- 在内存中创建了一个
'ABC'
的字符串;- 在内存中创建了一个名为
a
的变量,并把它指向'ABC'
。下面程序
a = 'ABC' b = a a = 'XYZ' print(b) # >>> ABC
执行过程:
执行
a = 'ABC'
,解释器创建了字符串'ABC'
和变量a
,并把a
指向'ABC'
:
执行
b = a
,解释器创建了变量b
,并把b
指向a
指向的字符串'ABC'
:
执行
a = 'XYZ'
,解释器创建了字符串’XYZ’,并把a
的指向改为'XYZ'
,但b
并没有更改:
所以,最后打印变量
b
的结果是'ABC'
了。
深浅拷贝
Python当中对于拷贝,按照拷贝数据类型分为两种类型。一种是数字和字符串的拷贝,另一种就是列表、元组、字典等其他数据结构类型的拷贝。
当然按照深浅也分深拷贝和浅拷贝。
一、数组和字符串拷贝
1、= 赋值
举例:
a1 = 123
a2 = 123
print(id(a1))
print(id(a2))
结果:(在你的电脑试验和这里的地址肯定不同)
140723824476816
140723824476816
原因是Python有一个重用机制,对于同一个数字,Python并不会开辟一块新的内存空间,而是维护同一块内存地址,a1,a2变量指向数字123的地址。等同于a2 = a1
,使用字符串赋值也是同理。
结论:对于通过用 = 号赋值,数字和字符串在内存当中用的都是同一块地址。
2、浅拷贝
举例:
import copy # 使用浅拷贝需要导入copy模块
a1 = 123
a3 = copy.copy(a1) # 使用copy模块里的copy()函数就是浅拷贝
print(id(a1))
print(id(a3))
结果:(在你的电脑试验和这里的地址肯定不同)
140723824476816
140723824476816
发现地址还是一样。
结论:对于浅拷贝,数字和字符串在内存当中用的也是同一块地址,和上面讲的原理相同。
3、深拷贝
举例:
import copy
a1 = 123
a4 = copy.deepcopy(a1) # 深拷贝是用copy模块里的deepcopy()函数
print(id(a1))
print(id(a4))
结果:(在你的电脑试验和这里的地址肯定不同)
140723824476816
140723824476816
发现地址还是一样。
结论:对于深拷贝,数字和字符串在内存当中用的也是同一块地址,和上面讲的原理相同。
综上所述,对于数字和字符串的赋值、浅拷贝、深拷贝在内存当中用的都是同一块地址。
二、字典,列表,元组等数据结构类型拷贝
1、=赋值
举例:
dict1 = {'k1': 'wu', 'k2': 123, 'k3': {1: 'alex', 2: 678}}
dict2 = dict1
print(id(dict1))
print(id(dict2))
结果:(在你的电脑试验和这里的地址肯定不同)
2331901131440
2331901131440
dict2变量只是指向了同一份字典数据的地址,如下图所示:
理解了上面的图我们看一下下面的代码:
dict1 = dict1['k3']
dict2 = dict1
dict2[3] = 'hiahia'
print(dict1)
我们逐步看怎么执行的:
dict1 = dict1['k3']
dict2 = dict1
dict2[3] = 'hiahia'
print(dict1)
所以结果是:
{1: 'alex', 2: 678, 3: 'hiahia'}
2. 浅拷贝
举例:
import copy
dict1 = {"k1": "wu", "k2": 123, "k3": {1: 'alex', 2: 678}}
dict3 = copy.copy(dict1) # 浅拷贝
print("第一层字典的内存地址:")
print(id(dict1))
print(id(dict3))
print("第二层嵌套的列表的内存地址:")
print(id(dict1["k3"]))
print(id(dict3["k3"]))
结果:(在你的电脑试验和这里的地址肯定不同)
第一层字典的内存地址:
2331901131872
2331901132088
第二层嵌套的列表的内存地址:
2331901155272
2331901155272
结果发现字典第一层确实被拷贝了,变成了两个,但第二层还是共用相同的数据,如下图:
结论:所以对于浅拷贝,字典、列表、元组等类型,它们只拷贝第一层地址。
3、深拷贝
举例:
import copy
dict1 = {"k1": "wu", "k2": 123, "k3": {1: 'alex', 2: 678}}
dict4 = copy.deepcopy(dict1) # 深拷贝
print("第一层字典的内存地址:")
print(id(dict1))
print(id(dict4))
print("第二层嵌套的列表的内存地址:")
print(id(dict1["k3"]))
print(id(dict4["k3"]))
print("第三层嵌套的列表的内存地址:")
print(id(dict1["k3"][1]))
print(id(dict4["k3"][1]))
结果:(在你的电脑试验和这里的地址肯定不同)
第一层字典的内存地址:
2331901131872
2331901132376
第二层嵌套的列表的内存地址:
2331901155272
2331901091976
第三层嵌套的列表的内存地址:
2331901150912
2331901150912
发现一二层被拷贝了,最底层的数字和字符串数据依然共用,如下图:
结论:对于深拷贝,字典、列表、元组等类型,它里面嵌套多少层,就会拷贝多少层出来,但是最底层的数字和字符串地址不变。