04-python基础语法_3.内存相关+深浅拷贝
4-1-3 内存相关+深浅拷贝
(一).Outline
1.内存相关
2.深浅拷贝
(二).Content
1.内存相关
1.1 赋值意味着重新开辟一个新的内存空间。但python中有特例 -缓存机制。
但是在python中,由于python内部性能优化,为了避免浪费内存,存在一个缓存机制。对于一些常用的int、str、float(浮点型)而言,不用再重新开辟内存空间,而是共用1个内存空间。list/dict/set/bool/tuple若赋值,则仍需重新开辟1个新的内存空间。
缓存机制内的3种数据范围如下:
-
int:-5 -256以内的数字;叫小数据池。???py3.6.8 & py2.7无论任何数字全部缓存了。???
-
str:只有当‘str中含有非字母、下划线的符号,同时乘了一个大于1的数字’时,才会重新开辟内存空间。其他case均在缓存机制内。
-
float:后补。
示例一:赋值 -重新开辟1个内存空间。适用于list/dict/set/bool/tuple以及范围外的int/str/float。
# 写在前面:
# 1.等号代表赋值。不管是同一个变量,还是不同的变量,=均代表赋值。
# 2.在python中,若赋值,除了范围内的int/str/float不需要重新开辟内存空间外,其它list/dict/set/bool /tuple均需要重新开辟一块内存空间。
# (1).赋值 -需要重新开辟内存空间的:list/dict/set/bool/tuple + 范围外的int/str/float。
# list/dict/set/bool/tuple:
v1 = [11, 22, 33]
v2 = [11, 22, 33]
print(id(v1), id(v2))
# 结果:(60714248L, 61296392L) # 每次在电脑内存中开辟空间都是随机的,故内存地址也是随机的。 # 每次开辟的空间都不一样。故2次打印的内存地址也会不同。
v1 = {11, 22, 33}
v2 = {11, 22, 33}
v1 = (11, 22, 33)
v2 = (11, 22, 33)
v1 = False
v2 = False
print(v1 is v2) # # True?????????????。。。。。。。。。。。。。。。。。。。。。。。???
v1 = {'name': '吕布', 'age':18}
v2 = {'name': '吕布', 'age':18}
# 范围外的int/str/float:
v1 = 257
v2 = 257
print(v1 is v2) # True???。。。。。。。。。。。在终端是False,在pycharm中是True?????。。。
v1 = '$' * 8
v2 = '$' * 8
print(v1 is v2) # False
print(v1 == v2) # True # 以上情况均是重新开辟内存空间。2者的内存地址不同。
# 另外,若给同一个变量重新赋值,在没有其他变量使用原内存空间的情况下,计算机会将此内存空间进行垃圾回收。
v1 = [1, 2, 3] # 内存垃圾 -待回收。
v1 = [1, 2, 3]
或:
v1 = [1, 2, 3, 4] # 内存垃圾 -待回收。
v1 = [1, 2, 3]
示例二:特例。
# (2).赋值 -不需要重新开辟内存空间的:范围内的int/str/float。
# a.-5 - 256以内的数字。
v1 = 99
v2 = 99
# b.常用字符串(不含非字母、下划线以外的符号,同时不做乘法计算:乘以一个大于1的数字)。
v1 = 'zxcv'
v2 = 'zxcv' # 以上2种情况,v1,v2共用1个内存空间,2者内存地址相同。
1.2 两个变量共用一个内存空间,然后修改此内存里存储的内容/赋值。
Case1:两个变量共用一个内存空间,然后做内部修改。-老巢变了,都变。
写在前面:仅list/dict/set存在此情况。
v1 = [1, 2]
v2 = v1 # 代表v1,v2共用一个内存空间。
v1 = [1, 2]
v2 = v1
v1.append(666)
print(v2) # v2变了 -含666。因为是在2者共用的内存空间内部做的修改。
练习题: 2种修改方式。
v1 = [1, 2, 3]
v2 = [11, 22, v1] # v2[-1]和v1共用一个内存空间。
v1.append(666) # 老巢变了,则都变。
print(v2) # [11, 22, [1, 2, 3, 666]]
v1 = [1, 2, 3]
v2 = [11, 22, v1]
v2[-1].append(666) # 这是通过v2[-1]找到的老巢,然后进行修改。
print(v1) # [1, 2, 3, 666]
# 写在最后:不管是在哪边修改,只要是老巢变了,则2者都变。
Case2:两个变量共用一个内存空间,然后赋值。-老巢没变,则不变。
写在前面:int/bool/str/tuple只能赋值,不能做内部修改。
但list/dict/set两种情况(既可赋值也可做内部修改)都存在。
v1 = [1, 2]
v2 = v1
v1 = [11, 22, 33]
print(v2) # v2没变 -因为v1是重新赋值,而不是修改老巢。只要老巢不变,v2就不变!
# 结果:[1, 2]
练习题:2种赋值方式。
v1 = [1, 2, 3]
v2 = [11, 22, v1]
v1 = 999 # 重新赋值。老巢没变。v2没变。
print(v2) # [11, 22, [1, 2, 3]]
v1 = [1, 2, 3]
v2 = [11, 22, v1]
v2[-1] = 999 # 改的只是v2这边的元素指向(重新赋值),老巢没变。故,v1不变。
print(v1) # [1, 2, 3]
# 写在最后:不管是在哪边赋值,只要是老巢没变,则未被赋值的那个就不变。
Case1 & Case2综合练习题:
# 1.共用一个内存空间,然后做内部修改。-老巢变了,则都变。 # 2种修改方式。
v1 = [1, 2]
v2 = [2, 3]
v3 = [11, 22, v1, v2, v1]
v1.append(666)
print(v3) # v3变了 -含666。因为老巢变了。
v1 = [1, 2]
v2 = [2, 3]
v3 = [11, 22, v1, v2, v1]
v3[2].append(7)
print(v1) # v1变了 -含7。因为老巢变了。
# 2.共用一个内存空间,然后赋值。-老巢没变,则不变。 # 2种赋值方式。
v1 = [1, 2]
v2 = [2, 3]
v3 = [11, 22, v1, v2, v1]
v1 = 100
print(v3) # v3没变。因为老巢没变。
v1 = [1, 2]
v2 = [2, 3]
v3 = [11, 22, v1, v2, v1]
v3[2] = 100
print(v1) # v1没变。因为改变的只是v3[2]的指向,老巢没变。
# 写在最后:判断变量是否发生改变,只需看2者共用的那个内存空间有没有做修改即可(即看老巢变没变)。
1.3 练习题 -1:可变/不可变数据类型共用一个内存空间。
# 一.list/dict/set(可变) -共用一个内存空间,然后赋值/做内部修改。-判断结果如何,只需看老巢变没变!!
# 1.
v1 = [1, 2, 3]
v2 = v1
v3 = v1
v1.append(999) # v2,v3都变了。因为老巢变了。
print(v2, v3)
# 2.
v1 = [1, 2, 3]
v2 = v1
v3 = v1
v1 = [1, ] # 重新赋值。# v2,v3没变。因为老巢没变。
print(v2, v3)
# 3.
v1 = [1, 2, 3]
v2 = v1
v3 = v1
v2 = [1, ] # 重新赋值。# v2,v3没变。因为老巢没变。
print(v1, v3)
# 4.
v1 = [1, 2, 3]
v2 = v1
v3 = v2 # v3指向的依然是[1, 2, 3]这个内存空间。
v2 = [1, ]
print(v1, v3) # v1,v3没变。因为老巢没变。
# 5.
v1 = {1, 2, 3}
v2 = v1
v1.add(999) # 修改老巢。
print(v1, v2) # v1,v2都变了。因为老巢变了。
# 6.
v1 = {1, 2, 3}
v2 = v1
new_set = v1.intersection([1, 22, 3]) # 取交并补集是生成一个新的set,即重新去开辟一个内存空间。而
print(v1, v2) # v1,v2没变。因为老巢没变。 # 不是在原有set上进行修改。
# 二.str/int/tuple/bool(不可变) -共用一个内存空间,然后赋值。-结果均不变。因为老巢不会变!!
# 因为:不可变数据类型不管做任何操作,均不会改变原数据(即不可变类型的老巢永远不会被修改),而是生成新的数据(即会去重新开辟一个内存空间放修改后的数据的内存地址)。
# 1.
v1 = 'lvbu' # str是不可变数据类型。
v2 = v1
new_str = v1.upper() # 是去重新开辟一个内存空间,放LVBU的内存地址。而不是在原有lvbu上做操作。
print(v1, v2) # 不变。因为老巢没变。
# 对比可变数据类型:
v1 = [1, 2, 3] # list是可变数据类型。
v2 = v1
v1.append(666) # 是对原有数据进行修改(即修改老巢)。
print(v1, v2) # 都变了。因为老巢变了。
# 2.
v1 = 'lvbu'
sequence = v1[0:2] # # 是去重新开辟一个内存空间,放lv的内存地址。而不是在原有lvbu上做操作。
print(v1) # 没变。因为老巢没变。
1.3 练习题 -2:可变数据类型共用一个内存空间 -多重嵌套。
# list/dict/set(可变) -共用一个内存空间,然后赋值/做内部修改。-判断结果如何,只需看老巢变没变!!
# 可变数据类型 -多重嵌套。
# ps:多重嵌套这种,画图一定要画到最底层元素!
# 练习1.
v1 = [1, 2, 3]
v2 = v1
v1[0] = [11, 22] # 修改老巢里面的内部元素。
print(v1, v2) # v1,v2都变了。因为老巢变了。
# 写在最后:虽然v1,v2的指向没变,但它们共同指向的这个内存空间里的内部元素指向变了。故2者均会随之改变。
# 练习2.
v1 = [1, 2, [88, 99], 3]
v2 = v1
v1[0] = 10 # 修改老巢里面的内部元素。
print(v1, v2) # v1,v2都变了。因为老巢变了。
v1 = [1, 2, [88, 99], 3]
v2 = v1
v1[2] = '吕布' # 修改老巢里面的内部元素。
print(v1, v2) # v1,v2都变了。因为老巢变了。
v1 = [1, 2, [88, 99], 3]
v2 = v1
v1[2][0] = 666 # 修改老巢里面的内部元素。
print(v1, v2) # v1,v2都变了。因为老巢变了。
# 写在最后:虽然v1,v2的指向没变,但它们共同指向的这个内存空间里的内部元素指向变了。故2者均会随之改变。
# 练习3.
v1 = [1, 2]
v2 = [1, 2, v1]
v1[0] = '吕布'
print(v2) # v2变了。因为老巢变了。
v1 = [1, 2]
v2 = [1, 2, v1]
v2[-1] = 999 # 改变指向,重新赋值。
print(v1) # v1没变。因为老巢没变。
v1 = [1, 2]
v2 = [1, 2, v1]
v2[-1][0] = 999 # 通过v2[-1][0]找到老巢,并修改。
print(v1) # v1变了。因为老巢变了。
# 写在最后:判断最后结果变没变,只需看老巢变没变即可!!
1.4 id is ==
-
id -id()是查看某个数据的内存地址。
# 定义:在Python中,id是内存地址。可以利用id()内置函数去查询一个数据的内存地址: # 示例: name = '吕布' name_id = id(name) # 通过内置方法获取name变量对应的值在内存中的编号。 print(name_id) # 49941360 这就是name在内存中的编号。
-
is -比较两边的内存地址是否相等。
v1 = [1, 2] v2 = [1, 2] print(v1 is v2) # False v1 = [1, 2] v2 = v1 print(v1 is v2) # True
-
== -比较的两边的数值是否相等。
v1 = [1, 2] v2 = [1, 2] print(v1 == v2) # True v1 = [1, 2] v2 = v1 print(v1 == v2) # True
2.深浅拷贝
2.0格式
import copy # 引入copy模块。
a = '吕布'
b = copy.copy(a) # 浅copy
c = copy.deepcopy(a) # 深copy
2.1str/int/bool此3种不可变数据类型,深浅copy相同。-均是共用一份内存空间。
# 以str为例:
import copy
a = '吕布'
b = copy.copy(a) # 浅copy
c = copy.deepcopy(a) # 深copy
print(a is b) # True
print(a is c) # True
# 写在最后:按说结果应该都是False,因为copy过来后重新开辟了一个新的内存空间。
# 但是由于python的小数据池缘故(为了防止浪费内存),对常用的这些str/int/bool数据,不会再重新开辟内存空间,而是沿用原来的内存空间。
# 故,str/int/bool 深/浅copy后的数据的id和原有数据id相同(因为是共用1个内存空间)。
2.2list/set/dict此3种可变数据类型的深浅copy。
写在前面:深copy只有在list/set/dict中存在嵌套list/set/dict时才有意义。(因为它copy的是所有的可变类型)
ps-1: 浅copy:只copy空壳+第一层元素。内部的所有元素均不copy。
ps-2: 深copy:是将内部所有的可变类型的数据全都copy一份。不可变数据延用原来的(python小数据池的缘故)。
# 示例一:拷贝list-无嵌套。-->深/浅copy相同。
# 浅copy:只copy空壳+第一层元素;
# 深copy:是将内部所有的可变类型的数据全都copy一份。不可变数据延用原来的。
import copy
v1 = [1, 2, 3]
v2 = copy.copy(v1) # 浅copy
v3 = copy.deepcopy(v1) # 深copy
print(v1 == v2) # True
print(v1 is v2) # False
print(v1[0] is v2[0]) # True
# 示例二:拷贝list中嵌套list。-->深/浅copy不同。
# 浅copy:只copy空壳+第一层元素;
# 深copy:是将内部所有的可变类型的数据全都copy一份。不可变数据延用原来的。
import copy
v1 = [1, 2, 3, [11, 22]]
v2 = copy.copy(v1) # 浅copy
v3 = copy.deepcopy(v1) # 深copy
print(v1 == v2) # True
print(v1 == v3) # True
print(v1 is v2) # False
print(v1 is v3) # False
print(v1[-1] is v2[-1]) # True
print(v1[-1] is v3[-1]) # False
print(v1[-1][0] is v3[-1][0]) # True
# 示例三:拷贝list中嵌套dict。-->深/浅copy不同。
# 浅copy:只copy空壳+第一层元素;
# 深copy:是将内部所有的可变类型的数据全都copy一份。不可变数据延用原来的。
import copy
v1 = [1, 2, 3, {'k1': 11, 'k2': 22}]
v2 = copy.copy(v1) # 浅copy
v3 = copy.deepcopy(v1) # 深copy
print(v1 == v2) # True
print(v1 == v3) # True
print(v1 is v2) # False
print(v1 is v3) # False
print(v1[-1] is v2[-1]) # True
print(v1[-1] is v3[-1]) # False
print(v1[-1] == v3[-1]) # true
print(v1[-1]['k1'] is v3[-1]['k1']) # True
2.3tuple的深浅copy -特殊情况(很少用到)。
Case 1:tuple的所有元素里没有可变数据类型。-同str。
# tuple里没有可变数据类型。
# 同str/int/bool:深/浅copy均与原数据共用一个内存空间。
# 原因:因为python小数据池的缘故,tuple为不可变类型,故对其copy不会重新开辟一个新的内存空间。
import copy
v1 = (1, 2, 3, )
v2 = copy.copy(v1)
v3 = copy.deepcopy(v1)
print(v1 is v2) # True
print(v1 is v3) # True
Case 2:tuple的元素里含有可变数据类型。-特殊!!
# tuple的元素里含有可变数据类型。
# 浅copy:同str。-延用原有数据。
# 深copy:copy可变数据 + 元组空壳。不可变数据延用原来的。-特殊!!
import copy
v1 = (1, 2, [11, 22], 3, )
v2 = copy.copy(v1)
v3 = copy.deepcopy(v1)
print(v1 is v2) # True
print(v1 is v3) # False
print(v1[2] is v3[2]) # False
print(v1[2][0] is v3[2][0]) # True
posted on 2019-06-13 22:45 insensate_stone 阅读(107) 评论(0) 编辑 收藏 举报