python中is和==的区别

python有很多运算符,其中is和==用的会比较多。此二者有些时候可以通用,作为相等比值来使用。但是有些时候二者是不能划等号的:比如:

a = 257
b = 257
print(a is b)
print(a == b)

out1: False
out2: True

很显然能看到a is b与a == b的返回值并不相等。这是为什么呢?

首先在讲is和==之前,我们要知道python的对象包含 三个基本要素id(身份标识),type(对象类型),value(对象值)

==比较操作符和is同一性运算符区别

  • ==是python标准操作符中的比较操作符,用来比较判断两个对象的value(值)是否相等,例如下面两个字符串间的比较:
>>> a = 'cheesezh'
>>> b = 'cheesezh'
>>> a == b
True
  • is也被叫做同一性运算符,这个运算符比较判断的是对象间的唯一身份标识,也就是id是否相同。通过对下面几个list间的比较,你就会明白is同一性运算符的工作原理:
>>> x = y = [4,5,6]
>>> z = [4,5,6]
>>> x == y
True
>>> x == z
True
>>> x is y
True
>>> x is z
False
>>>
>>> print id(x)
3075326572
>>> print id(y)
3075326572
>>> print id(z)
3075328140

前三个例子都是True,这什么最后一个是False呢?x、y和z的值是相同的,所以前两个是True没有问题。至于最后一个为什么是False,看看三个对象的id分别是什么就会明白了。

  • 最后看一下这个,再来理解一下is和==的区别:
>>> a = 1 #a和b为数值类型
>>> b = 1
>>> a is b
True
>>> id(a)
14318944
>>> id(b)
14318944
>>> a = 'cheesezh' #a和b为字符串类型
>>> b = 'cheesezh'
>>> a is b
True
>>> id(a)
42111872
>>> id(b)
42111872
>>> a = (1,2,3) #a和b为元组类型
>>> b = (1,2,3)
>>> a is b
False
>>> id(a)
15001280
>>> id(b)
14790408
>>> a = [1,2,3] #a和b为list类型
>>> b = [1,2,3]
>>> a is b
False
>>> id(a)
42091624
>>> id(b)
42082016
>>> a = {'cheese':1,'zh':2} #a和b为dict类型
>>> b = {'cheese':1,'zh':2}
>>> a is b
False
>>> id(a)
42101616
>>> id(b)
42098736
>>> a = set([1,2,3])#a和b为set类型
>>> b = set([1,2,3])
>>> a is b
False
>>> id(a)
14819976
>>> id(b)
14822256

总结

>>> x = -5
>>> y = -5
>>> x is y
True
>>> 
>>> x = -6
>>> y = -6
>>> x is y
False
>>> 
>>> x = 256
>>> y = 256
>>> x is y
True
>>> x = 257
>>> y = 257
>>> x is y
False
>>> 

# 数字限于[-5, 257)

事实上Python 为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。而Python 对小整数的定义是 [-5, 257),只有数字在-5到256之间它们的id才会相等,超过了这个范围就不行了,同样的道理,字符串对象也有一个类似的缓冲池,超过区间范围内自然不会相等了。

总的来说,只有数值型和字符串型,并且在通用对象池中的情况下,a is b才为True,否则当a和b是int,str,tuple,list,dict或set型时,a is b均为False。


拓展:

python在字符串上也有一种机制,为python intern机制,又称字符串驻留。

  • 它是python解释器用来提高字符串使用的效率和使用性能做的优化。其原理就是同样的字符串对象仅仅会被保存一份,放在一个字符串储蓄池中,是共用的。
  • intern机制的优缺点:
    • 优点:在创建新的字符串时,会现在缓存池中查找是否有值相同的对象(标识符,即只包含字母、数字、下划线的字符),如果有,则直接拿过来用(引用),避免频繁的创建和销毁内存,提升效率。
    • 缺点:在拼接字符串时,或者在改动字符串时会极大的影响性能。原因是字符串在Python当中是不可变对象,所以对字符串的改动不是inplace(原地)操作,需要新开辟内存地址,新建对象。这也是为什么拼接字符串的时候不建议用‘+’而是用join()。join()是先计算出全部字符串的长度,然后再一一拷贝,仅仅创建一次对象。
  • 储蓄池本身是一个字典结构。解释器内部对intern机制的使用是有考究的,有些情况会触发,有些情况不会触发。
  • 在shell环境中,通常仅仅包括字母、数字、下划线的字符串才会被intern,一般不超过20个字符。

看下面一些栗子:

# 字符串仅包含字母、数字、下划线
>>> a = 'wtf_'
>>> b = 'wtf_'
>>> a is b
True
>>> a = 'wtf_1'
>>> b = 'wtf_1'
>>> a is b
True
>>> b = 'abcdefghijklmnopqrstuvwxyz_1'
>>> a = 'abcdefghijklmnopqrstuvwxyz_1'
>>> a is b
True

# 字符串拼接
>>> a = 'opq'
>>> b = 'o' + 'pq'
>>> id(a)
66832910
>>> id(b)
66832910

# 用变量去连接字符串和字符串连接字符串是不一样的
>>> a = 'opq'
>>> b = 'op'
>>> c = b + 'q'
>>> d = 'op' + 'q'
>>> a is c
False
>>> a is d
True
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 1

可以把is作为判断内存地址来用,区分是否是同一个内存地址

9个月前 评论
runstone (楼主) 9个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!