Python比较符与拷贝问题

is VS ==

简单来说,==比较的是值大小,is比较的是内存地址,总的来说,is运算符的速度大于==运算符,这是在于is运算符没办法重载,而运行==运算符时,程序会先去搜索__eq__函数,如果没有重载,就会直接比较大小,由于python很多内置的函数都重载了__eq__函数

值得注意的是,对于整型数字来说,a is b为True的结论,只适用于-5到256之间,比如:

1
2
3
4
5
6
7
8
>>> a = 257
>>> b = 257
>>> id(a)
2314450519952
>>> id(b)
2314450941424
>>> a is b
False

原因在于,出于性能考虑,python内部会维持一个-5到256的数组,起到一个缓存的作用。每次你去创建一个-5到256范围的整型数字时,python会去这个数组中取值,而不是去创建一个新的内存。

浅拷贝与深拷贝

浅拷贝:是指重新分配一块内存,创建一个新的对象,里面的元素是对原对象中子对象的引用。如果原对象中元素是不可变类型,不会产生影响,如果是可变类型,会带来新对象的改变。

深拷贝:指重新分配一块新的内存地址,创建一个新的对象和新的元素,和原对象没有任何关联。

注意,浅拷贝带来的变化只针对原对象的子对象是可变类型数据,例如嵌套列表

1
2
3
4
5
6
7
8
>>> a = [[1, 2], 3]
>>> b = a[:]
>>> a[0].append(4)
>>> b
[[1, 2, 4], 3]
>>> a.append(5)
>>> b
[[1, 2, 4], 3]
1
2
3
4
5
6
7
8
9
10
>>> a = [[1, 2], (3, 4)]
>>> a[1] += (5, )
>>> a
[[1, 2], (3, 4, 5)]
>>> b = a[:]
>>> a[1] += (5, )
>>> b
[[1, 2], (3, 4, 5)]
>>> a
[[1, 2], (3, 4, 5, 5)]

而深拷贝不会受到任何影响

1
2
3
4
5
6
7
>>> from copy import deepcopy
>>> a
[[1, 2], (3, 4, 5, 5)]
>>> b = deepcopy(a)
>>> a[0].append(3)
>>> b
[[1, 2], (3, 4, 5, 5)]

深拷贝也不是完美的,如果被拷贝对象是自身,程序有可能会陷入无限循环

1
2
3
4
5
6
7
>>> x = [1]
>>> x.append(x)
>>> x
[1, [...]]
>>> y = deepcopy(x)
>>> y
[1, [...]]

x为一个无限嵌套的列表,深拷贝的时候,程序并没有报stack overflow的现象,这是在于深拷贝函数内部维护这一个字典,记录已经拷贝对象和ID,拷贝过程中,如果字典里存储了将要拷贝对象,会直接将字典中内容返回

而在使用==比较深拷贝之后的无限嵌套列表时,会抛出异常RecursionError: maximum recursion depth exceeded in comparison,这是在于列表中==会去遍历每个值的大小再去比较,而列表时无限嵌套的,所以会抛出异常

1
2
3
4
5
import copy
x = [1]
x.append(x)
y = copy.deepcopy(x)
print(x == y) #RecursionError: maximum recursion depth exceeded in comparison
-------------本文结束感谢您的阅读-------------