python深拷贝和浅拷贝的区别不可变对象_Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别)?...

欢迎关注公众号:数据科学中文社区

我从“可变对象的原处修改”这里引入,这是一个值得注意的问题。

上一小节我们谈到,赋值操作总是存储对象的引用,而不是这些对象的拷贝。由于在这个过程中赋值操作会产生相同对象的多个引用,因此我们需要意识到“可变对象”在这里可能存在的问题:在原处修改可变对象是可能会影响程序中其他引用该对象的变量。如果你不想看到这种情景,则你需要明确的拷贝一个对象,而不是简单赋值。

X = [1,2,3,4,5]

L = ['a', X, 'b']

D = {'x':X, 'y':2}

print(L)

print(D)

['a', [1, 2, 3, 4, 5], 'b']

{'y': 2, 'x': [1, 2, 3, 4, 5]}

在这个例子中,我们可以看到列表[1,2,3,4,5]有三个引用,被变量X引用、被列表L内部元素引用、被字典D内部元素引用,那么利用这三个引用中的任意一个去修改列表[1,2,3,4,5],也会同时改变另外两个引用的对象,例如我利用L来改变[1,2,3,4,5]的第二个元素,运行的结果就非常明显。

X = [1,2,3,4,5]

L = ['a', X, 'b']

D = {'x':X, 'y':2}

L[1][2] = 'changed'

print(X)

print(L)

print(D)

[1, 2, 'changed', 4, 5]

['a', [1, 2, 'changed', 4, 5], 'b']

{'x': [1, 2, 'changed', 4, 5], 'y': 2}

引用是其他语言中指针的更高层的模拟。他可以帮助你在程序范围内任何地方传递大型对象而不必在途中产生拷贝。

可是如果我不想共享对象引用,而是想实实在在获取对象的一份独立的复制,该怎么办呢?

能想到这一层确实很不错,其实这个很简单,常用的手法有以下几种:

第一种方法:分片表达式能返回一个新的对象拷贝,没有限制条件的分片表达式能够完全复制列表

L = [1,2,3,4,5]

C = L[1:3]

C[0] = 8

print(C)

print(L)

[8, 3]

[1, 2, 3, 4, 5]

L = [1,2,3,4,5]

C = L[:]

C[0] = 8

print(C)

print(L)

[8, 2, 3, 4, 5]

[1, 2, 3, 4, 5]

可以看出,用分片表达式得到了新的列表拷贝C,对这个列表进行修改,不会改变原始列表L的值。

第二种方法:字典的copy方法也能够实现字典的完全复制:

D = {'a':1, 'b':2}

B = D.copy()

B['a'] = 888

print(B)

print(D)

{'a': 888, 'b': 2}

{'a': 1, 'b': 2}

第三种:内置函数list可以生成拷贝

L = [1,2,3,4]

C = list(L)

C[0] = 888

print(C)

print(L)

[888, 2, 3, 4]

[1, 2, 3, 4]

最后我们看一个复杂一些的例子

B通过无限制条件的分片操作得到了A列表的拷贝,B对列表内元素本身的修改,不会影响到A,例如修改数值,例如把引用换成别的列表引用:

L = [1,2,3,4]

A = [1,2,3,L]

B = A[:]

B[1] = 333

B[3] = ['888','999']

print(B)

print(A)

print(L)

[1, 333, 3, ['888', '999']]

[1, 2, 3, [1, 2, 3, 4]]

[1, 2, 3, 4]

但是如果是这种场景呢?

L = [1,2,3,4]

A = [1,2,3,L]

B = A[:]

B[1] = 333

B[3][1] = ['changed']

print(B)

print(A)

print(L)

[1, 333, 3, [1, ['changed'], 3, 4]]

[1, 2, 3, [1, ['changed'], 3, 4]]

[1, ['changed'], 3, 4]

因为B的最后一个元素也是列表L的引用(可以看做获取了L的地址),因此通过这个引用对所含列表对象元素进行进一步的修改,也会影响到A,以及L本身

所以说,无限制条件分片以及字典的copy方法只能进行顶层的赋值。就是在最顶层,如果是数值对象就复制数值,如果是对象引用就直接复制引用,所以仍然存在下一级潜藏的共享引用现象。

如果想实现自顶向下,深层次的将每一个层次的引用都做完整独立的复制,那么就要使用copy模块的deepcopy方法。

import copy

L = [1,2,3,4]

A = [1,2,3,L]

B = copy.deepcopy(A)

B[3][1] = ['changed']

print(B)

print(A)

print(L)

[1, 2, 3, [1, ['changed'], 3, 4]]

[1, 2, 3, [1, 2, 3, 4]]

[1, 2, 3, 4]

这样,就实现了递归的遍历对象来复制他所有的组成成分,实现了完完全全的拷贝,彼此之间再无瓜葛。

没想到简单的赋值还有这么多的坑!最后再来总结总结:普通的=赋值得到的其实仅仅是共享引用;无限条件的分片、字典copy方法和内置函数list这三种方法可以进行顶层对象的拷贝,而deepcopy可以彻底的实现自顶向下的完全拷贝。

关于数据科学更系统、更深入的探讨可进入我们的专栏《Python数据科学之路》:酱油哥:来吧,一起踏上Python数据科学之路​zhuanlan.zhihu.com

本专栏模仿美剧剧集编排分为五季,第一季:Python编程语言核心基础、第二季:Python数据分析基本工具、第三季:Python语言描述的数学基础、第四季:机器学习典型算法专题、第五季:实战热点深度应用。

有任何问题也可以咨询微信号:zhangyumeng0422

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值