python初学者深拷贝与浅拷贝与引用的踩坑

在理解深拷贝与浅拷贝前,先理解下什么是引用。
大家学python的时候都会看到这一次句话。python的所有变量都是引用,引用是什么?
python内存模型简单的可以理解为堆空间,以及栈空间。堆空间存的是对象数据,栈空间存的的是该对象数据的内存地址,该地址指向堆空间的对象数据,这个指向也称为引用。举一些例子来看。

示例1:
a=[1,2,3]
b=a
print(id(b))
print(id(a))

如下图a和b都是存的是1000内存地址,而不是对象本身。
在这里插入图片描述
那会有个疑问,如果把列表改了,那是否引用的地址也变了

示例2:
a=[1,2,3]
b=a
print(id(b))
print(id(a))
a[1]=4
print(id(b))
print(id(a))

答案是否定的。因为列表里的数据也都是引用,而不是对象本身。我们通过修改list里的数据不会影响a本身指向的地址,而是修改了a[1]的对象地址。
再看个示例:
通过切片的方式将数据传给c

示例3:
    a=[1,2,3]
    b=a
    print(id(b))
    print(id(a))
    c=a[:]
    print(id(b))
    print(id(a))
    print(id(c))

结果发现,c虽然跟a的值是一样的,但是却不是同个内存地址。原因是因为列表的切片是创建一个新的列表序列,再将新的对象数据地址赋值给c。

将上述的a数据换个二维数组

示例4:
    a=[1,2,[4,5,6]]
    b=a
    print(id(b))
    print(id(a))
    a[1]=4
    print(id(b))
    print(id(a))
    c=a[:]
    print(id(b))
    print(id(a))
    print(id(c))
    c[2][1]=8
    print(a)
    print(c)

[1, 4, [4, 8, 6]]
[1, 4, [4, 8, 6]]
结果可以发现,c变了,a也还是会变。
原因是因为列表切片后生成的新的列表对象,指向的地址也会变。但列表里面存的对象地址,并没有变更。所以还是跟a是一样的。简单的理解如下图。a跟c 都是存了x1,x2,x3地址。 c[2][1]=8意味着x32对象引用变了,由5地址变为了8地址。所以所有有x3引用的数据都会变更。

在这里插入图片描述
所以,可以简单认为变量的赋值以及切片都是浅拷贝,复制的是对象数据的内存地址。
可以通过打印列表元素地址可以看出是否一样:

    for i in c:
        print(id(i))
    for i in a:
        print(id(i))

那如果修改c而不影响a,那就需要将a对象全部拷贝一份数据到新的内存中。这个过程叫做深拷贝。需要通过deepcopy来完成

    c=copy.deepcopy(a)
    for i in c:
        print(id(i))
    for i in a:
        print(id(i))

这样你会发现。两个列表的list的元素存放地址是不一样的。
那我们再换个示例:

示例5:
    a=[1,2,[4,5,6]]
    c=[1,2,[4,5,6]]
    print(id(a))
    print(id(c))
    for i in c:
        print(id(i))
    for i in a:
        print(id(i))

a会跟c一样吗?答案是不一样的。因为直接复制既不是深拷贝也不是浅拷贝,而是直接创建新的对象数据给地址引用。
当然python为了省内存,[-5~256]的范围内,都是固定的地址,不会创建新的地址。还有字符串的变量申明也一样,如果存在类似的字符串,python不会额外创建新的字符串内存空间,而是会直接引用。需要注意的是,pycharm会对这些整数以及字符串做优化处理,结果可能是一样的,所以以下示例请使用命令行运行。

    a=1234
    b=1234
    print(id(a))
    print(id(b))
    a='abcd'
    b='abcd'
    print(id(a))
    print(id(b))

结果内存地址是不一样的。

总结:

python所有的变量=直接赋值都是创建新的对象,引用新对象地址(保留的整数以及字符串除外),=直接赋值没有浅拷贝深拷贝说法。
变量赋值a=b 都是引用拷贝对象的地址,也就是浅拷贝。
列表或者元组切片是第一层深拷贝,第二层以上浅拷贝。
如果想要完全的拷贝另一个列表或者元组的变量,则需要deecopy深拷贝。

疑问:如果是for 循环append后是深拷贝还是浅拷贝呢,欢迎留言

    a=[1,2,[4,5,6]]
    c=[]
    for i in a:
        c.append(i)
    c[1]=22
    c[2][1]=8
    print(a)
    print(c)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值