zoukankan      html  css  js  c++  java
  • Python学习笔记(十一)——赋值、深拷贝与浅拷贝

    Python中,对象赋值实际上是对象的引用。即当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了对这个对象的引用地址。简单来讲就是说,在给变量赋值的时候,我们会给变量值开辟一个存放空间,当我们调用这个变量的时候实际上是在调用这个空间中的值:比如 a = "test" ,那么test会有一个存放的空间位置ID,当b = a 的时候实际上是把b的指针指向了test的存储空间ID,而不是重新给b开辟一个新空间存放数据。

    常见的有三种方法:

    • 直接赋值:其实就是对象的引用(别名)。

    • 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。

    • 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。

    一、直接赋值;默认浅拷贝传递对象的引用而已,原始列表改变,被赋值的其它对象也会做相同的改变

    a = [1,2,['test1','test2']]
    b = a
    print(id(a))  #查看存储空间的id
    print(id(b)) #查看存储空间的id
    print('第一次的值:',a)
    print('第一次的值:',b)
    a.append('4')
    print('第二次的值:',a)
    print('第二次的值:',b)
    
    运行结果:
    18635464
    18635464
    第一次的值: [1, 2, ['test1', 'test2']]
    第一次的值: [1, 2, ['test1', 'test2']]
    第二次的值: [1, 2, ['test1', 'test2'], '4']
    第二次的值: [1, 2, ['test1', 'test2'], '4']

    二、浅拷贝:copy浅拷贝,没有拷贝子对象,所以原始数据改变,子对象会改变

    a = [1,2,['test1','test2']]
    # b = a.copy()
    b = copy.copy(a)
    
    print('a的ID值:',id(a))
    print('b的ID值:',id(b))
    print('第一次的值a:',a)
    print('第一次的值b:',b)
    print('a所有元素的下标值:',[id(ele) for ele in a])
    print('b所有元素的下标值:',[id(ele) for ele in b])
    a[0]=3
    a[2].append('test3')
    print('第二次的值a:',a)
    print('第二次的值b:',b)
    print('a所有元素的下标值:',[id(ele) for ele in a])
    print('b所有元素的下标值:',[id(ele) for ele in b])
    
    运行结果:
    a的ID值: 18639688
    b的ID值: 10909960
    第一次的值a: [1, 2, ['test1', 'test2']]
    第一次的值b: [1, 2, ['test1', 'test2']]
    a所有元素的下标值: [1750270448, 1750270480, 18743880]
    b所有元素的下标值: [1750270448, 1750270480, 18743880]
    第二次的值a: [3, 2, ['test1', 'test2', 'test3']]
    第二次的值b: [1, 2, ['test1', 'test2', 'test3']]
    a所有元素的下标值: [1750270512, 1750270480, 18743880]
    b所有元素的下标值: [1750270448, 1750270480, 18743880]
    
    分析:
    首先,创建一个变量a,并指向了一个list类型的对象;
    然后通过copy模块里面的浅拷贝函数copy(),对a指向的对象进行浅拷贝,然后浅拷贝生成的心对象赋值给b变量(
    1、浅拷贝会创建一个新的对象,所以id看的值是不一样的;2、对象中的元素,浅拷贝就只会使用原始元素的引用(内存地址),也就是说"a[i] is b[i]);
    最后对a进行修改的时候,由于list的第一个元素是不可变类型,所以will对应的list的第一个元素会使用一个新的对象1750270512;由于第三个元素是可变类型,修改操作不会产生新的对象,所以a的修改结果会相应的反应到b上

    三、深度拷贝,包含对象里面的对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变

    import copy
    a = [1,2,['test1','test2']]
    b = copy.deepcopy(a)
    
    print('a的ID值:',id(a))
    print('b的ID值:',id(b))
    print('第一次的值a:',a)
    print('第一次的值b:',b)
    print('a所有元素的下标值:',[id(ele) for ele in a])
    print('b所有元素的下标值:',[id(ele) for ele in b])
    a[2].append('test3')
    print('第二次的值a:',a)
    print('第二次的值b:',b)
    print('a所有元素的下标值:',[id(ele) for ele in a])
    print('b所有元素的下标值:',[id(ele) for ele in b])
    
    运行结果:
    a的ID值: 18574152
    b的ID值: 18674696
    第一次的值a: [1, 2, ['test1', 'test2']]
    第一次的值b: [1, 2, ['test1', 'test2']]
    a所有元素的下标值: [1750270448, 1750270480, 18674632]
    b所有元素的下标值: [1750270448, 1750270480, 18574536]
    第二次的值a: [1, 2, ['test1', 'test2', 'test3']]
    第二次的值b: [1, 2, ['test1', 'test2']]
    a所有元素的下标值: [1750270448, 1750270480, 18674632]
    b所有元素的下标值: [1750270448, 1750270480, 18574536]
    
    分析:
    首先,创建一个变量a,并指向了一个list类型的对象;
    然后通过copy模块里面的深拷贝函数deepcopy(),对a指向的对象进行深拷贝,然后深拷贝生成的对象赋值给b变量(1、深拷贝会创建一个新的对象,所以id看的值是不一样的;2、对象中的元素,深拷贝都会重新生成一份,而不是简单的使用原始元素的引用(内存地址),也就是说"a[2] is not b[2]);
    最后对a进行修改的时候,由于第三个元素是可变类型,修改操作不会产生新的对象,但是由于"a[2] is not b[2]",所以a的修改不会影响b

    四、总结

    • Python中对象的赋值都是对对象内存地址的引用传递
    • 使用copy.copy(a)或者a.copy(),可以进行对象的浅拷贝,它复制了对象,但对于对象中的元素,依然使用原始的引用.
    • 如果需要完全拷贝父对象及其子对象(它里面的所有元素(包含元素的子元素)),可以使用copy.deepcopy()进行深拷贝

    参考链接:

    https://www.cnblogs.com/wilber2013/p/4645353.html

  • 相关阅读:
    实验一、词法分析实验
    词法分析程序新
    词法分析程序
    我对编译原理的理解
    Unity3d数据存储 PlayerPrefs,XML,Json数据的存储与解析
    Unity3d网络总结(三) 封装Socket创建简单网络
    Unity3d网络总结(二)使用NetWorking代码搭建简单的网络模块
    Unity3d网络总结(一) NetWork组件使用
    Unity加载AB包
    unity编辑器拓展
  • 原文地址:https://www.cnblogs.com/beginner-boy/p/12520063.html
Copyright © 2011-2022 走看看