• 10.小数据池和深浅拷贝


    小数据池和深浅拷贝

    一、深浅拷贝

    1.赋值

    赋值就是一个容器有多个标签

    lst = [1,2,3,[6,7,8]]
    

    执行以上程序,内容空间发生的变化就是下图:

    image-20190624164715237

    一个列表用两个标签,通过标签lst 找到的和标签lst1找到的是同一个,图中的那些一长串数字就是内存地址,Python中是通过内存地址来查看值

    lst1 = lst                  #赋值
    lst[-1].append(9)           #-1索引处追加元素 值为9
    

    我们通过lst这个标签找到这个列表然后添加一个9,再通过lst1找到这个列表也就多了一个9 因为lst和lst1都是贴在一个地方

    image-20190624164938391

    2.浅拷贝

    浅拷贝就是只拷贝第一层的元素

    lst = [1,2,3,[6,7,8]]
    # lst2 = lst[:] # 浅拷贝
    lst2 = lst.copy()
    

    image-20190624165932135

    图中橙色的是新开辟的空间,浅蓝色的是数字类型,红色的列表类型

    这样就是浅拷贝,浅拷贝只把原列表中记录的内存地址拿到一个新开辟的列表中

    lst = [1,2,3,[6,7,8]]
    lst2 = lst[:]
    lst.append(9)
    

    image-20190624170320641

    为什么lst2中没有添加,是因为咱们先进行的浅拷贝,浅拷贝把原列表中有的内存地址复制了一份放到新开辟的空间中,后期对原列表添加的内容新列表里是不会有的,再看看下边的例子

    lst = [1,2,3,[6,7,8]]
    lst2 = lst.copy()
    lst[1] = "22"
    

    image-20190624170640334

    我们修改成字符串"22" 就是在列表中将以前的内存地址更换成新开辟的空间地址

    lst = [1,2,3,[6,7,8]]
    lst2 = lst.copy()
    lst[-1].append(9)
    

    image-20190624171104866

    因为我们对里边的列表进行修改,列表本身就是可变的数据类型,我们通过原列表修改最里层的小列表,小列表进行变化,新开辟的列表里存放就是小列表中的内存地址.在去查看的时候就有变动

    3.深拷贝

    深拷贝是自己单独开辟了一个新的空间,我们现在修改原列表对新开辟的列表没有任何影响.

    来看看深拷贝是怎样的操作

    import copy
    lst = [1,2,3,[6,7,8,9]]
    lst2 = copy.deepcopy(lst)
    

    image-20190624172845500

    通过上面的各种测试,总结以下规律:

    • 赋值:
      • 两个或多个变量名指向同一个内存地址,有一个操作内存地址的值进行改变,其余的变量名在查看的时候都进行更改(换个变量名,但内存地址不变
    • 浅拷贝:
      • 只拷贝列表中第一层的内存地址,原列表修改了不可变数据类型,新开辟的列表不进行变动,因为只是在原列表中将内存地址进行修改了,新开辟的列表中的内存地址还是用的之前的内存地址(只复制第一层,小列表的内存地址还是一致的,旧的改 新的跟着改,但第一层的内容旧的改新的不变
      • 原列表对可变数据类型进行了添加,新开辟的列表中存放就是可变数据类型的地址,在去查看的时候就发现进行更改了
    • 深拷贝:
      • 不管你修改原数据的不可变类型还是可变类型,新开辟的空间中都不会进行改变,因为可变数据类型新开辟了一个空间(完全复制!内存地址全部另辟空间,旧的改 不影响新的

    二、小数据池

    == id is

    ==我们都用过了,就是进行判断的。判断两边的值是否一样,例如

    a = 10
    b = 10
    print(a == b) 
    
    a = "yangmei"
    b = "yangmei"
    print(a == b)
    

    这样就是查看==两边的值是否一样.

    我们再来看看id是个什么东西,我们知道在定义一个变量的时候,内存空间中其实是开辟了一块空间,这个开辟的空间是有号码的,我们来试一下

    name = "yangmei"
    print(id(name)) #查看内存地址  
    # 2646445653360
    

    is 也是判断,只不过这次判断的是两边值得内存地址是否相同,我们来看看

    a = 10
    b = 10
    print(id(a))  # 140726887389120
    print(id(b))  # 140726887389120
    print(a is b)  # True
    # 获取的结果是True是因为a和b的内存地址是相同的
    

    发现一个问题 == 和 is 都是True啊,这个is是判断内存地址是否一样,Python考虑到我们会经常定义一些值,需要开辟空间和销毁空间,它底层就维护了一个小数据池,这个小数据就是规定一个区间使用的是同一个内存地址,比如小数据池中数字的区间范围是 -5 ~ 256,我们刚刚测试的10在区间内,所以获取到的是相同的内存地址,我们试一试不在范围的数字

    a = 500
    b = 500
    print(id(a)) # 2756092656496
    print(id(b)) # 2756092656496
    print(a is b) # True
    

    不再区间内,怎么内存地址还是一样的啊。这就要说说python的另一个机制 — 代码块

    代码块是防止我们频繁的开空间降低效率设计的,当我们定一个变量需要开辟空间的时候,它会先去检测我们定义的这个在空间中有没有进行开辟,如果没有开辟就开辟一个空间,如果内存中开辟过就使用同一个。

    一个文件,一个函数,一个模块,一个类,终端中一行就是一个代码块

    代码块支持:

    • 字符串:
      • 定义字符串的时候内容,长度任意内存地址相同。
      • 字符串进行乘法的时候总长度 <=20 内存地址相同。
      • 中文,特舒符号 乘法的时候只能乘以1或 0
    • 数字:
      • 相同的数字内存地址相同
    • 布尔值:
      • 相同的内存地址相同

    这就是我们为什么在pycharm中测试的时候都是True,我们现在去终端上测试一下数字的范围

    image-20190624142057797

    当代码块和小数据池两个在一起,先执行代码块

    我们知道了代码块支持的数据类型和支持怎样的操作,现在来看看小数据池的支持数据类型和范围:

    小数据支持:

    • 字符串:
      • 纯字母和数字的时候长度任意,内存地址相同。
      • Python36纯字母和数字乘法总长度 <= 20 内存地址相同。
      • Python37纯字母和数字乘法总长度 <= 4096 内存地址相同。
      • 中文和特殊符号乘法的时候只能乘以 0 内存地址相同
    • 数字:
      • -5 ~ 256
    • 布尔值:
      • True
      • False

    小数据池和代码块都是Python内置的,咱们开发的时候不使用,他们统称为驻留机制,有了小数据池和代码块能够提升Python的效率

  • 相关阅读:
    Python Revisited Day 13 (正则表达式)
    Python Revisited Day 06 (面向对象程序设计)
    Python Revisited (变量)
    Python Revisited Day 05(模块)
    Python Revisited Day 04 (控制结构与函数)
    Python Revisited Day 03 (组合数据类型)
    Numpy
    Python Revisited Day 01
    Python3使用openpyxl读写Excel文件
    Python3操作YAML文件
  • 原文地址:https://www.cnblogs.com/yangte/p/13499685.html
Copyright © 2020-2023  润新知