博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
给妹子讲python-S01E11赋值与对象拷贝
阅读量:6640 次
发布时间:2019-06-25

本文共 2821 字,大约阅读时间需要 9 分钟。

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

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

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] = 8print(C)print(L)[8, 3][1, 2, 3, 4, 5]L = [1,2,3,4,5]C = L[:]C[0] = 8print(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'] = 888print(B)print(D){'a': 888, 'b': 2}{'a': 1, 'b': 2}

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

L = [1,2,3,4]C = list(L)C[0] = 888print(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] = 333B[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] = 333B[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 copyL = [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中的比较、相等性和真值问题。

L1 = [1,2,['A','B']]L2 = [1,2,['A','B']]L3 = L1print(L1 == L2, L1 is L2)print(L1 == L3, L1 is L3)True FalseTrue True

==测试两个对象值的相等性,即递归的比较所有内嵌对象;

is 测试两个对象的一致性,即是否是在一个内存空间。

真值问题

Python把任意的空数据结构视为假,把任何非空数据结构视为真

使用None,一般都起到一个空的占位符的作用,在真值判断的时候为false

L = [None] * 10print(L)[None, None, None, None, None, None, None, None, None, None]

内置bool函数可以测试一个对象是真还是假

print(bool([1,2]))print(bool(True))print(bool(None))print(bool('abcde'))TrueTrueFalseTrue

原文发布时间为:2018-08-15

本文作者:给妹子讲python
本文来自云栖社区合作伙伴“”,了解相关信息可以关注“”。

转载地址:http://deovo.baihongyu.com/

你可能感兴趣的文章
post和get
查看>>
宋体语句那些苦逼日子的学习(C语言)06
查看>>
Linux中的计划任务at命令的用法
查看>>
使用游标——游标FOR循环
查看>>
5月收集
查看>>
Javascript获取DropDownList选项信息
查看>>
javaScript 正则表达式2
查看>>
原创:Qt自定义拖放
查看>>
策略模式
查看>>
(转)RGB接口和i80接口的区别
查看>>
6月21小记
查看>>
RESTful API 设计最佳实践
查看>>
定义文件XML——从简单开始
查看>>
android辅助开发工具包介绍
查看>>
JSP中scope属性 scope属性决定了JavaBean对象存在的范围
查看>>
Linux设备驱动开发详解-Note(11)--- Linux 文件系统与设备文件系统(3)
查看>>
实习第一天之数据绑定:<%#Eval("PartyName")%>'
查看>>
POJ 2318 TOYS (计算几何,叉积判断)
查看>>
第四章 Spring与JDBC的整合
查看>>
开源的Android视频播放器
查看>>