Python对象的比较和拷贝
本文内容存在主观理解,详细请查阅官网文档
比较(==
VS is
)
==
操作符是比较对象的值是否相等,而is
比较的事对象的身份标识是否相等,即它们是否是同一个对象,是否指向同一个内存地址。例如:
>>>a = 6
>>>b = 6
>>>a == b
True
>>>id(a)
2545626147408
>>>id(b)
2545626147408
>>>a is b
True
对于整型数字来说,
a is b
为True
的结论只适用于\(-5-256\),因为出于对性能优化的考虑,Python内部会对\(-5-256\)的整型维持一个数组,起到缓存的作用,因此,我们每次创建一个\(-5-256\)范围内的整型数字时,Python会从这个数组返回对应的引用,而不会开辟一块新的内存空间。如果数字超过这个范围,Python就会重新开辟两块内存区域>>>a = 257 >>>b = 257 >>>a is b False >>>id(a) 2076692650576 >>>id(b) 2076701657552
比较操作符is
的效率要优于==
,因为is
运算符不能被重载,这样Python就不需要去寻找程序中其它地方是否重载了比较运算符。但是a==b
相当于执行a.__eq__(b)
,而Python中大部分的数据类型都会重载__eq___这个函数
,比如列表,这个函数会遍历列表中的元素,比较它们的顺序和值是否相等。
对于不可变的变量,如果使用==
或is
比较过,那么结果是否是保持不变呢?
>>>a = (1, 2, [3, 4])
>>>b = (1, 2, [3, 4])
>>>a == b
True
>>>a[-1].append(5)
>>>a == b
元组类型是不可变的,但是元组可以嵌套可变数据类型,当我们修改了元组中的某个可变类型,元祖本身也就改变了,之前的比较结果就不再适用。
拷贝
浅拷贝
>>>a = [1, 2, 3]
>>>b = list(a)
>>>b
[1,2,3]
>>>a == b
True
>>>a is b
False
这里,b
是a
的浅拷贝。对于可变的序列,可以通过切片实现浅拷贝,例如:
>>>a = [1, 2, 3]
>>>b = a[:]
>>>a == b
True
>>>a is b
False
或者使用copy.copy()
函数,适用于任何的数据类型
>>>import copy
>>>a = [1, 2, 3]
>>>b = copy.copy(a)
对于元祖来说,使用
tuple()
或:
不会创建一份浅拷贝,而是指向相同元祖的引用>>>a = (1, 2, 3) >>>b = tuple(a) >>>a == b True >>>a is b True
综上,浅拷贝是指重新分配一块内存,创建一个新的对象,里面的元素是原对象中子对象的引用(如果原对象中的元素可变,浅拷贝会带来一些副作用)
>>> a = [[1, 2], (3, 4)]
>>> b = list(a)
# 因为浅拷贝里的元素是对原对象元素的引用,因此b和a中的元素指向同一个列表和元组对象
>>> a.append(10)
# a中新增元素10,但因为b和a整体是不用的对象,所以对b没有影响
>>> a[0].append(20)
# 对a的第一个元素新增20,b和a中的第一个元素指向同一个列表,因此b的第一个元素也会增加20
>>> a
[[1, 2, 20], (3, 4), 10]
>>> b
[[1, 2, 20], (3, 4)]
>>> a[1] += (5, 6)
# 元组是不可变类型,这里是对a中的元组拼接让后**创建了一个新的元组**作为a的第二个元素,而b中没有引用新元组,所以b不发生改变
>>> a
[[1, 2, 20], (3, 4, 5, 6), 10]
>>> b
[[1, 2, 20], (3, 4)]
想要避免浅拷贝带来的副作用,可以使用深拷贝完整拷贝一个对象,即重新分配一块内存,创建一个新的对象,并将原对象中的元素以递归的方式通过创建新的子对象拷贝到新对象中,这样新对象与原对象之间没有任何关联。
Python中使用copy.deepcopy()
实现对象的深度拷贝。
>>> import copy
>>> a = [[1, 2], (3, 4)]
>>> b = copy.deepcopy(a)
>>> a.append(10)
>>> a[0].append(20)
>>> a
[[1, 2, 20], (3, 4), 10]
>>> b
[[1, 2], (3, 4)]
此时,a
与b
完全独立,无论a
如何变化,b
都不变。但是深度拷贝也存在一些问题,比如如果拷贝对象中存在指向自身的引用,就有可能陷入无限循环。
标签:20,Python,对象,拷贝,copy,True From: https://www.cnblogs.com/euler0525/p/17010512.html>>> import copy >>> a = [1] >>> a.append(a) >>> a [1, [...]] >>> b = copy.deepcopy(a) >>> b [1, [...]] >>> a == b Traceback (most recent call last): File "<stdin>", line 1, in <module> RecursionError: maximum recursion depth exceeded in comparison # 进行比较操作符`==`的时候,会递归地遍历对象的所有值并逐一比较。而Python为了防止栈崩溃,到了限定的递归层数,解释器会报错。
在这个例子中,并没有出现栈溢出的情况,因为
deepcopy
会维护一个字典,记录已经拷贝的对象,如果字典中已经存储了要拷贝的对象,就直接从字典中返回。(参考https://github.com/python/cpython/blob/main/Lib/copy.py)