Python中is和==的區別及intern機制

  • 2019 年 11 月 6 日
  • 筆記

Python中is和==的區別?

說明

好久沒更新公眾號了,原諒我忘記了。。。

寫在前面

嗨小夥伴們好久不見,本來今天是更新一篇關於字元串的文章,但是還沒寫完,先更新一篇關於is和==區別的文章,因為字元串文章中會涉及到本篇的部分內容。另外最近會勤於更新,大家不要催更了......

is和==的區別

1、is 比較的是地址,注意小整數池和字元串,一般重複創建的時候會指向同一個對象。

t1 = 123t2 = 123print(t1 is t2)  # Trueprint(id(t1), id(t2))  # 1789477024 1789477024print("*********************************")

因為在python中存在intern機制,適用於小整數池和字元串,也就是說創建對象的時候會先在小整數池中查找,如果存在就返回,否則就會新建對象。記住僅僅適用於小整數池和字元串,對於list是不使用的

2、== 比較的是值,其實==重載了對象的__eq__方法,而這個方法比較的是對象的值。

t2 = 123print(t1 == t2)  # Trueprint("*********************************")

因為我們在使用==對兩個對象進行值判斷的時候,其實就是調用了__eq__這個魔法函數,查看一下str中的__eq__魔法函數:

def __eq__(self, *args, **kwargs):

再來看一下list對象中的__eq__魔法函數:

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

你會發現兩者是一模一樣,因此==就是對值進行判斷,只要值相等就是True。

我們再來看一個例子,在判斷類的類別時,是使用is,而不是==,原因在於type(obj)返回的是某個類,而類是一個全局唯一的變數,這種做法可以避免很多未知的錯誤:

pass  person = Person()if type(person) is Person:  # 記住這裡最好使用is,不要使用==

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機制。

後續會更新字元串的文章,請大家等待哈,下篇文章再見~~~

Tags: