理解Python中is 与 == 的区别
在Python中,有一个问题会被经常问到,那就是运算符is
与==
有什么区别?
很多人都知道他们都是比较两个对象是否相等,说起来都是比较对象,但是分不清什么时候用哪个,其实非常容易区分只要记住一点。
==
是比较运算符,它比较的是两个对象中的值是否是相等的。而is
是同一性运算符,比较的是两个对象的id值是否相等或者说比较的是对象的内存空间地址是否相等。
为了更好更深刻的理解,我们还是从几个例子出发:
a = 1, 2, 3
b = 1, 2, 3
print(f'The result of (a == b) is: {a == b}')
print(f'The result of (a is b) is: {a is b}')
# 输出如下:
# The result of (a == b) is: True
# The result of (a is b) is: False
我们可以发现,a和b的值都是一个值为1, 2, 3的元组类型。所以在用比较运算符==
比较时显示为True
。
但是用is
同一性运算符比较时,结果却为False
,因为尽管它们的值是相等的,但是这两个对象各自的内存空间地址是不相等的。我们可以向下面这样用Python内置的id()
函数来查看独享的内存地址信息。
print(f'a: {id(a)}, b: {id(b)}')
# 输出如下:
# a: 112163566792, b: 112166450376
可以发现,2个对象的内存地址不一样,所以用is
比较的时候结果为False
。
现在,我们创建了一个新对象c,然后把a赋值给c,也就是说a和c两个变量指向的是同一个对象地址,当我们用id()函数查看内存地址时显示地址的确一样,所以再次用is运算符比较的时候显示为True
,而且反过来推如果is
比较下来相等,那么用==
比较自然也相等。
c = a
print(f'The result of (a is c) is: {a is c}')
print(f'a: {id(a)}, c: {id(c)}')
# 输出内容:
# The result of (a is c) is: True
# a: 112163566792, c: 112163566792
这里必须要提一下,还有一个看似很奇怪的比较问题也经常被拿来反问一些新手。
在Python里明明新创建一个变量对象就会开辟一个内存空间,那么用is
比较的时候应该是不同的地址,返回应该是False,可是下面为什么他们返回True而且内存地址是相同的呢?
s1 = 'hello'
s2 = 'hello'
print(f's1: {id(s1)}, s2: {id(s2)}')
print(f'The result of (s1 is s2) is: {s1 is s2}')
# 输出内容:
# s1: 112167134296, s2: 112167134296
# The result of (s1 is s2) is: True
而如果赋值的是长一点的字符串时,可以看到确实得到了正常该有的结果(确实创建了两个不同对象,用is判断确实应该返回False)。
s3 = 'hello, Anders'
s4 = 'hello, Anders'
print(f's3: {id(s3)}, s4: {id(s4)}')
print(f'The result of (s3 is s4) is: {s3 is s4}')
# 输出内容:
# s3: 112166728944, s4: 112167126384
# The result of (s3 is s4) is: False
这是因为前一种情况下Python的字符串驻留机制起了作用。对于较小的字符串或者整数,为了提高系统性能Python会保留其值的一个副本,当创建新的字符串的时候直接指向该副本即可。所以 "hello" 在内存中只有一个副本,s1 和 s2 的 id 值相同,而 "hello, Anders" 是长字符串,不驻留内存,Python中各自创建了两个对象s2 和 s4,所以他们的值相同但 id 值不同。
那么,看看下面这个奇怪的例子。前面我们解释到了,对于较小的字符串或者整数,为了提高系统性能Python会保留其值的一个副本,也就不会创建新的内存空间了,所以你会看到如下示例给变量a1,b1赋值为256时,实际上他们是一个内存空间地址,自然is比较下来为True,可是为什么当给a2,b2按一样的方式赋值整数只是赋的值为257结果却为False呢?
a1 = 256
b1 = 256
print(f'The result of (a1 is b1) is: {a1 is b1}')
a2 = 257
b2 = 257
print(f'The result of (a2 is b2) is: {a2 is b2}')
# 输出如下:
# The result of (a1 is b1) is: True
# The result of (a2 is b2) is: False
因为,整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。
Python 对小整数的定义是 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。在一个 Python 的程序中,无论这个整数处于LEGB中的哪个位置,所有位于这个范围内的整数使用的都是同一个对象。同理,单个字母也是这样的。