Python高级编程(32):Python中的is和==的区别?
is和==的区别
1、is 比较的是地址,注意小整数池和字符串,一般重复创建的时候会指向同一个对象。
1 | t1 = 123 |
因为在python中存在intern机制,适用于小整数池和字符串,也就是说创建对象的时候会先在小整数池中查找,如果存在就返回,否则就会新建对象。记住仅仅适用于小整数池和字符串,对于list是不使用的
2、== 比较的是值,其实==重载了对象的__eq__
方法,而这个方法比较的是对象的值。
1 | t1 = 123 |
因为我们在使用==对两个对象进行值判断的时候,其实就是调用了__eq__
这个魔法函数,查看一下str中的__eq__
魔法函数:
1 | def __eq__(self, *args, **kwargs): # real signature unknown |
再来看一下list对象中的__eq__
魔法函数:
1 | def __eq__(self, *args, **kwargs): # real signature unknown |
你会发现两者是一模一样,因此==就是对值进行判断,只要值相等就是True。
我们再来看一个例子,在判断类的类别时,是使用is,而不是==,原因在于type(obj)返回的是某个类,而类是一个全局唯一的变量,这种做法可以避免很多未知的错误:
1 | class Person: |
python的intern机制
由于变量的存储机制,python增加了字符串的intern机制,也就是说值同样的字符串对象(整数也实用)仅仅会保存一份,而且是共用的,这也决定了字符串必须是不可变对象,其实仔细想一想,就和数值类型一样,同样的数值仅仅要保存一份即可了,不是必须用不同对象来区分。
我们来看几个例子加深我们的认识,开启Python的IDE,必须使用Python自带的交互式环境,不能使用Pycharm:
1 | >>> a = 'test' |
从结果中可以看出它们实际上是同一个对象,所以用is判断就是正确。
intern机制的优点是:在创建新的字符串对象时,会先在缓存池里面找是否有已经存在的值相同的对象(标识符,即只包含数字、字母、下划线的字符),如果有则直接拿过来用(引用),避免频繁的创建和销毁内存,提升效率。
缺点是在拼接字符串时或者在改动字符串时会极大的影响性能。原因是字符串在Python当中是不可变对象,所以对字符串的改动不是inplace(原地)操作,需要新开辟内存地址,即新建对象。这也是为什么拼接字符串的时候不建议用‘+’而是用join()。join()是先计算出全部字符串的长度,然后再一一拷贝,仅仅创建一次对象。
再来看这个例子:
1 | #这里只是演示了包含空格的情况,实际上除了字母,数字,下划线以外的都是这个情况 |
你可能会好奇怎么是False,因为这里的字符串里面含有空格,导致不会触发intern机制。 也就是说字符串中没有空格则会默认开启intern机制,有则就不会开启了。
你现在可能会好奇Python为什么会这么做呢?因为Python的内置函数intern()能显式的对任意字符串进行intern,就说明并不是实现难度的问题,解决这个问题最好是查看Python的源码,可以找到答案,在源代码StringObject.h中的注释能够找到:
1 | /* … … This is generally restricted tostrings that “looklike” Python identifiers, although the intern() builtincan be used to force interning of any string … … */ |
也就是说intern机制仅仅对那些看起来像是Python标识符的字符串对象才会触发。
我们再来看一个例子:
1 | >>> 'tes'+'t' is 'test' |
你可能会问为什么是这样?它们不都只是包含字母吗,没有空格应该是被主动intern的呀? 的确是不错,但是你忽略了一个事实。在第一个例子中,‘tes’ + ‘t’是在compile-time(编译时)求值的,被替换成了’test’,而在第二个例子中,a + ‘t’是在run-time(运行时)拼接的,导致没有主动触发intern机制。
我们再来看一下Python中的小整数的例子:
1 | >>> a = 257 |
在Python的小整数池[-5,256]这个范围内也是默认开启intern机制,也就是在创建对象的时候会先判断整数是否在小整数池中,是的话就共用同一个对象,否则就新建对象。
总结一下
1、is 比较的是地址,注意小整数池和字符串,一般重复创建的时候会指向同一个对象。
2、== 比较的是值,其实==重载了对象的__eq__
方法,而这个方法比较的是对象的值。
3、单词,即Python标识符是不可修改的,默认开启intern机制,是共用对象,当引用计数为0时自动被回收。
4、字符串(包含了除Python标识符以外的字符),不可修改,默认没有开启intern机制,也是当引用计数为0时自动被回收。
5、极少数特殊情况下(如上述最后一个例子时),也不会主动开启intern机制。
6、在Python的小整数池[-5,256]这个范围内也是默认开启intern机制。