zoukankan      html  css  js  c++  java
  • 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的效率

  • 相关阅读:
    面试高频题:讲讲项目中的技术难点?
    看完本文还不会安装mysql吗?
    spring中如何向一个单例bean中注入非单例bean
    一次性讲清楚spring中bean的生命周期之三:bean是如何实例化的
    java面试一日一题:字节java后端工程师面试题
    VMware 安装 Centos 7 虚拟机配置网络
    基于 Blazor 打造一款实时字幕
    MVP on Board 没用小技巧 👌
    数据治理实践:元数据管理架构的演变
    Apache Superset1.2.0教程(四)—— CentOS环境安装
  • 原文地址:https://www.cnblogs.com/yangte/p/13499685.html
Copyright © 2011-2022 走看看