龙空技术网

Python中is和==的区别及intern机制

啃饼思录 449

前言:

当前我们对“pythonbytestoint”大概比较重视,朋友们都需要知道一些“pythonbytestoint”的相关资讯。那么小编在网络上收集了一些有关“pythonbytestoint””的相关内容,希望各位老铁们能喜欢,大家快快来学习一下吧!

写在前面

本来今天是更新一篇关于字符串的文章,但是还没写完,先更新一篇关于is和==区别的文章,因为字符串文章中会涉及到本篇的部分内容。另外最近会勤于更新,大家不要催更了......

is和==的区别

1、is 比较的是地址,注意小整数池和字符串,一般重复创建的时候会指向同一个对象。

t1 = 123t2 = 123print(t1 is t2) # Trueprint(id(t1), id(t2)) # 1789477024 1789477024print("*********************************")l1 = [1,2,3]l2 = [1,2,3] # False,因为会重新声明一个对象# l2 = l1 # True,因为是赋值符号,就是指针指向print(l1 is l2) # Falseprint(id(l1), id(l2)) # 56509864 56511024

因为在python中存在intern机制,适用于小整数池和字符串,也就是说创建对象的时候会先在小整数池中查找,如果存在就返回,否则就会新建对象。记住仅仅适用于小整数池和字符串,对于list是不使用的

2、== 比较的是值,其实==重载了对象的__eq__方法,而这个方法比较的是对象的值。

t1 = 123t2 = 123print(t1 == t2) # Trueprint("*********************************")l1 = [1,2,3]l2 = [1,2,3] print(l1 == l2) # True

因为我们在使用==对两个对象进行值判断的时候,其实就是调用了__eq__这个魔法函数,查看一下str中的__eq__魔法函数:

 def __eq__(self, *args, **kwargs): # real signature unknown """ Return self==value. """ pass

再来看一下list对象中的__eq__魔法函数:

 def __eq__(self, *args, **kwargs): # real signature unknown """ Return self==value. """ pass

你会发现两者是一模一样,因此==就是对值进行判断,只要值相等就是True。

我们再来看一个例子,在判断类的类别时,是使用is,而不是==,原因在于type(obj)返回的是某个类,而类是一个全局唯一的变量,这种做法可以避免很多未知的错误:

class Person: passperson = Person()if type(person) is Person: # 记住这里最好使用is,不要使用== print("yes")

python的intern机制

由于变量的存储机制,python增加了字符串的intern机制,也就是说值同样的字符串对象(整数也实用)仅仅会保存一份,而且是共用的,这也决定了字符串必须是不可变对象,其实仔细想一想,就和数值类型一样,同样的数值仅仅要保存一份即可了,不是必须用不同对象来区分。

我们来看几个例子加深我们的认识,开启Python的IDE,必须使用Python自带的交互式环境,不能使用Pycharm:

>>> a = 'test'>>> b = 'tes' + 't'>>> print(id(a), id(b))>>> print(a is b)# 运行结果:# 51379584 51379584# True

从结果中可以看出它们实际上是同一个对象,所以用is判断就是正确。

intern机制的优点是:在创建新的字符串对象时,会先在缓存池里面找是否有已经存在的值相同的对象(标识符,即只包含数字、字母、下划线的字符),如果有则直接拿过来用(引用),避免频繁的创建和销毁内存,提升效率。

缺点是在拼接字符串时或者在改动字符串时会极大的影响性能。原因是字符串在Python当中是不可变对象,所以对字符串的改动不是inplace(原地)操作,需要新开辟内存地址,即新建对象。这也是为什么拼接字符串的时候不建议用‘+’而是用join()。join()是先计算出全部字符串的长度,然后再一一拷贝,仅仅创建一次对象。

再来看这个例子:

#这里只是演示了包含空格的情况,实际上除了字母,数字,下划线以外的都是这个情况>>> j = "hello world" >>> k = "hello world">>> print(id(j), id(k))# 93423920 95800760>>> print(j is k)# False

你可能会好奇怎么是False,因为这里的字符串里面含有空格,导致不会触发intern机制。 也就是说字符串中没有空格则会默认开启intern机制,有则就不会开启了。

你现在可能会好奇Python为什么会这么做呢?因为Python的内置函数intern()能显式的对任意字符串进行intern,就说明并不是实现难度的问题,解决这个问题最好是查看Python的源码,可以找到答案,在源代码StringObject.h中的注释能够找到:

/* … … This is generally restricted tostrings that “looklike” Python identifiers, although the intern() builtincan be used to force interning of any string … … */

也就是说intern机制仅仅对那些看起来像是Python标识符的字符串对象才会触发。

我们再来看一个例子:

>>> 'tes'+'t' is 'test'>>> True>>> a = 'tes'>>> a + 't' is 'test'>>> False

你可能会问为什么是这样?它们不都只是包含字母吗,没有空格应该是被主动intern的呀? 的确是不错,但是你忽略了一个事实。在第一个例子中,‘tes’ + ‘t’是在compile-time(编译时)求值的,被替换成了’test’,而在第二个例子中,a + ‘t’是在run-time(运行时)拼接的,导致没有主动触发intern机制。

我们再来看一下Python中的小整数的例子:

>>> a = 257>>> b= 257>>> print(id(a), id(b))94149312 94149488>>> a = 256>>> b = 256>>> print(id(a), id(b))1790200048 1790200048>>> a = -5>>> b = -5>>> print(id(a), id(b))1790195872 1790195872>>> a = -6 >>> b =-6>>> print(id(a), id(b))94148784 94149296

在Python的小整数池[-5,256]这个范围内也是默认开启intern机制,也就是在创建对象的时候会先判断整数是否在小整数池中,是的话就共用同一个对象,否则就新建对象。

总结一下

1、is 比较的是地址,注意小整数池和字符串,一般重复创建的时候会指向同一个对象。

2、== 比较的是值,其实==重载了对象的__eq__方法,而这个方法比较的是对象的值。

3、单词,即Python标识符是不可修改的,默认开启intern机制,是共用对象,当引用计数为0时自动被回收。

4、字符串(包含了除Python标识符以外的字符),不可修改,默认没有开启intern机制,也是当引用计数为0时自动被回收。

5、极少数特殊情况下(如上述最后一个例子时),也不会主动开启intern机制。

6、在Python的小整数池[-5,256]这个范围内也是默认开启intern机制。

标签: #pythonbytestoint