zoukankan      html  css  js  c++  java
  • Python浅拷贝和深拷贝

    面试的时候经常会问到深拷贝和浅拷贝,那么python的深拷贝和浅拷贝有什么区别呢?

    首先深拷贝和浅拷贝都是对象的拷贝,都会生成一个看起来相同的对象,他们本质的区别是拷贝出来的对象的地址是否和原对象一样,也就是地址的复制还是值的复制的区别。

    浅拷贝 A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.

    上面这段话是官方文档上的描述,有2个含义:

    • 1.浅拷贝会创建一个新的容器对象(compound object)
    • 2.对于对象中的元素,浅拷贝就只会使用原始元素的引用(内存地址)

    常见的浅拷贝操作有:

    • 使用切片操作[:]
    • 使用工厂函数(如list/dict/set)
    • copy模块的copy()方法

    深拷贝 A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

    上面这段话是官方文档上的描述,也是有2个含义:

    • 1.深拷贝和浅拷贝一样,都会创建一个新的容器对象(compound object)
    • 2.和浅拷贝的不同点在于,深拷贝对于对象中的元素,深拷贝都会重新生成一个新的对象

    可变和不可变对象

    可变对象是指,一个对象在不改变其所指向的地址的前提下,可以修改其所指向的地址中的值;

    不可变对象是指,一个对象所指向的地址上值是不能修改的,如果你修改了这个对象的值,那么它指向的地址就改变了,相当于你把这个对象指向的值复制出来一份,然后做了修改后存到另一个地址上了,但是可变对象就不会做这样的动作,而是直接在对象所指的地址上把值给改变了,而这个对象依然指向这个地址。

    在python中有6个标准数据类型,他们分为可变和不可变两类。

    • 不可变类型:Number(数字)String(字符串)Tuple(元组)
    • 可变类型:List(列表)Dictionary(字典)Set(集合)

    深拷贝和浅拷贝需要注意的地方就是可变元素的拷贝

    在浅拷贝时,拷贝出来的新对象跟原来的原对象的地址是不一样的,但是新对象里面的可变元素(如列表字典集合等)地址跟原对象中的可变元素地址是相同的,也就是说浅拷贝拷贝的是浅层次的数据结构(不可变元素),对象里面的可变元素作为深层次的数据结构并没有被拷贝到新的地址里面去,而是和原对象里的可变元素指向同一个地址,所以新对象或原对象里的可变元素做修改时,两个对象是同时改变的。

    浅拷贝使用 copy 模块的 copy 方法

    深拷贝使用 copy 模块的 deepcopy 方法

    import copy
    a = [1, 2, 3, 4, 6, ['a', 'b']]
    b = a #赋值
    c = copy.copy(a) #浅拷贝
    d = copy.deepcopy(a) #深拷贝
    a[5].append('c')
    print('a={}'.format(a), ',a的地址%s' % id(a), ',a中list的地址%s' % id(a[5]))
    print('b={}'.format(b), ',b的地址%s' % id(b), ',b中list的地址%s' % id(b[5]))
    print('c={}'.format(c), ',c的地址%s' % id(c), ',c中list的地址%s' % id(c[5]))
    print('d={}'.format(d), ',d的地址%s' % id(d), ',d中list的地址%s' % id(d[5]))
    运行结果:

    a=[1, 2, 3, 4, 6, ['a', 'b', 'c']] ,a的地址2404719790856 ,a中list的地址2404719790600
    b=[1, 2, 3, 4, 6, ['a', 'b', 'c']] ,b的地址2404719790856 ,b中list的地址2404719790600
    c=[1, 2, 3, 4, 6, ['a', 'b', 'c']] ,c的地址2404719792008 ,c中list的地址2404719790600
    d=[1, 2, 3, 4, 6, ['a', 'b']] ,d的地址2404719791944 ,d中list的地址2404719791880

    从程序的结果来看:

    列表a和b是赋值操作,两个对象地址是一样的,里面的可变元素地址也是相同的;

    a和c是浅拷贝操作,浅拷贝时拷贝了list外面一层,创建一个新的容器对象,所以a和c的地址是不一样的,对于容器里面的元素对象的可变元素作为深层次的数据结构并没有被拷贝到新的地址里面去,只会使用原始原始的引用,所以可以看到子元素的内存地址还是一样的。

    a和d是深拷贝操作,d的浅层次元素(不可变)和 深层次元素(可变)的地址和a,b,c都不一样,所以,a,b,c无论怎么修改,d都不会跟着改变,这就是深拷贝的结果。

    深拷贝和浅拷贝的不同点在于,深拷贝对于对象中的元素,深拷贝都会重新生成一个新的对象。

  • 相关阅读:
    十分钟开发一个调用Activity的PhoneGap插件
    Mac下MAMP初试体验
    探索Android中的Parcel机制(上)
    两个栈实现队列+两个队列实现栈----java
    php实现工厂模式
    Hibernate Criterion
    Android用户界面概览
    秒杀多线程第四篇 一个经典的多线程同步问题
    Java串口通信具体解释
    逗比之——程序猿装逼手冊1(0基础版)
  • 原文地址:https://www.cnblogs.com/cavaXu/p/14450951.html
Copyright © 2011-2022 走看看