顽石自雕

导航

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]

01

02

示例二:特例。

# (2).赋值 -不需要重新开辟内存空间的:范围内的int/str/float。
# a.-5 - 256以内的数字。
v1 = 99
v2 = 99

# b.常用字符串(不含非字母、下划线以外的符号,同时不做乘法计算:乘以一个大于1的数字)。
v1 = 'zxcv'
v2 = 'zxcv'   # 以上2种情况,v1,v2共用1个内存空间,2者内存地址相同。

03

1.2 两个变量共用一个内存空间,然后修改此内存里存储的内容/赋值。

Case1:两个变量共用一个内存空间,然后做内部修改。-老巢变了,都变。

写在前面:仅list/dict/set存在此情况。

v1 = [1, 2]
v2 = v1  # 代表v1,v2共用一个内存空间。

04

v1 = [1, 2]
v2 = v1 
v1.append(666)
print(v2)  # v2变了 -含666。因为是在2者共用的内存空间内部做的修改。

05

练习题: 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者都变。

06

Case2:两个变量共用一个内存空间,然后赋值。-老巢没变,则不变。

写在前面:int/bool/str/tuple只能赋值,不能做内部修改。

list/dict/set两种情况(既可赋值也可做内部修改)都存在

v1 = [1, 2]
v2 = v1
v1 = [11, 22, 33]
print(v2)  # v2没变 -因为v1是重新赋值,而不是修改老巢。只要老巢不变,v2就不变!
# 结果:[1, 2]

07

练习题: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]
# 写在最后:不管是在哪边赋值,只要是老巢没变,则未被赋值的那个就不变。

08

09

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者均会随之改变。

10

# 练习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者均会随之改变。

11

12

13

# 练习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变了。因为老巢变了。
# 写在最后:判断最后结果变没变,只需看老巢变没变即可!!

14

15

16

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

003

# 示例二:拷贝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

004.png

# 示例三:拷贝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

005.png

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

006.png

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

007.png

posted on 2019-06-13 22:45  insensate_stone  阅读(107)  评论(0编辑  收藏  举报