==和is的区别

等于(==)和 is 是 Python 中对象比较常用的两种方式。简单来说,’=='操作符比较对象之间的值是否相等,比如下面的例子,表示比较变量 a 和 b 所指向的是否相等。

a == b

'is’操作符比较的是对象的身份标识是否相等,即它们是否是同一个对象,是否指向同一个内存地址

在python中,通过函数 id(object)会获得当前对象的唯一标识,对于is操作符,就是相当于比较对象之间的id值是否相等

a = 10
b = 10
print(a==b)
# True
print(id(a))
print(id(b))
print(a is b)
# True

上面这个例子说明,在python中,先为10这个值开辟一块内存地址,然后a和b都是指向这块地址,所有==和is都为True。但是is也相等这种情况只是适用于值在-5~256之间,比如下面这个例子就is就为False

a = 257
b = 257
print(a == b)
#True
print(id(a))
print(id(b))
print(a is b)
#False

具体的原因:
python会对-5~256的整型维持一个数组,起到一个缓存的作用,所以每次创建一个-5到256的整型变量的时候,都会直接从数组拿出来引用,而不是从新开辟空间,起到了优化性能的作用。超过这个范围,就会新开辟内存,自然使用is的时候就不一样了。
此外,is一般使用于判断一个值是否为None.
使用is的耗时正常情况下比==更短。
具体的原因是:
'is’操作符不能被重载,就不用去找是否有重载比较符,也就是说,执行is的时候,就是执行两个变量的id(object)。
对于==来说,a == b 实际上执行的是a.eq(b),在python中大部分函数都会去重载这个函数,导致内部处理比较复杂一些,对于列表,就会使用__eq__函数会去遍历列表中的元素,比较它们的顺序和值是否相等。

浅拷贝(shallow copy)和深度拷贝(deep copy)

浅拷贝

浅拷贝:是指重新分配一块内存,创建一个新的对象,里面的元素是原对象中子对象的引用
下面是浅拷贝的几种方式
1、使用本身的构造器

l1 = [1, 2, 3]
l2 = list(l1)

s1 = set([1, 2, 3])
s2 = set(s1)

2、对于可变的序列,我们还可以通过切片操作符’:'完成浅拷贝

l1 = [1, 2, 3]
l2 = l1[:]

3、 自带的copy.copy()

import copy
l1 = [1, 2, 3]
l2 = copy.copy(l1)

浅拷贝注意事项
1、对于元组,使用 tuple() 或者切片操作符’:'不会创建一份浅拷贝,相反,它会返回一个指向相同元组的引用

t1 = (1, 2, 3)
t2 = tuple(t1)
print(t1 == t2)
#True
print(t1 is t2)
#True

2、浅拷贝的副作用

# 1
l1 = [[1, 2], (30, 40)]
l2 = list(l1)
# 2
l1.append(100)
# 3
l1[0].append(3)
 
print(l1)
#[[1, 2, 3], (30, 40), 100]
 
print(l2)
#[[1, 2, 3], (30, 40)]
 
 # 4
l1[1] += (50, 60)
print(l1)
#[[1, 2, 3], (30, 40, 50, 60), 100]
 
print(l2)
#[[1, 2, 3], (30, 40)]

第一步:初始化l1和l2,浅拷贝,所以 l2 中的元素和 l1 指向同一个列表和元组对象,但是整体是不同的两个对象。
第二步:l1.append(10),也只是l1这个对象有所改变,l2不改变,查看上面l1的值。
第三步:l1[0].append(3),这个时候,因为 l2 是 l1 的浅拷贝,l2 中的第一个元素和 l1 中的第一个元素共同指向同一个列表,因此 l2 中的第一个列表也会相对应的新增元素 3。操作后 l1 和 l2 都会改变。
第四步:l1[1] += (50, 60),因为元组是不可变的,所以这么操作后只剩l1有了新元组,l2并没有,所以操作后 l2 不变,l1 发生改变。

深拷贝

深度拷贝:是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联
深拷贝的方式使用copy.deepcopy()

import copy
l1 = [[1, 2], (30, 40)]
l2 = copy.deepcopy(l1)

深拷贝注意的点
1、深度拷贝自己,会陷入死循环,但是程序不会stack overflow,因为深度拷贝函数 deepcopy 中会维护一个字典,记录已经拷贝的对象与其 id。拷贝过程中,如果字典里已经存储了将要拷贝的对象,则会从字典直接返回。

import copy
x = [1]
x.append(x)
 
print(x)
#[1, [...]]
 
y = copy.deepcopy(x)
print(y)
#[1, [...]]

2、基于上面这个问题,当判断x == y的时候,程序的输出,应该是

RecursionError: maximum recursion depth exceeded in comparison

这是因为:’=='操作符则会递归地遍历对象的所有值并且逐一比较。但是python为了防止栈崩溃,限定了递归的层数,也就是说递归不会无休下去,所以,当递归的次数到了阈值后,就会跳出上述错误。