本文整理了許多字符串駐留的坑,部分整合自wtfpython英文版,并增加了大量的后續(xù)說明。
# example1:
>>> a ="wtf"
>>> b ="wtf"
>>> a is b
True
# example2:
>>> a ="wtf!"
>>> b ="wtf!"
>>> a is b
False
# example3:
>>> a, b ="wtf!","wtf!"
>>> a is b
True# 3.7 版本返回結果為 False.
# example4:
>>>'a'*20is'aaaaaaaaaaaaaaaaaaaa'
True
>>>'a'*21is'aaaaaaaaaaaaaaaaaaaaa'
False# 3.7 版本返回結果為 True
字符串的這些問題,像是在和你說 1 != 1 一樣坑爹。
究其原因,其實是CPython在編譯的時候會自動進行優(yōu)化,在某些情況下它會嘗試使用已經(jīng)存在的不可變對象,而不是創(chuàng)建一個新的對象,而恰好,字符串就是不可變對象。這種使用已存在的不可變對象的行為被稱為“駐留 ” 。
駐留的原本設計意圖是用于節(jié)省內(nèi)存的,但是確實有時候會坑到程序員。怎樣判斷自己的字符串會否被駐留呢?請看這份代碼:
https://github.com/python/cpython/blob/3.6/Objects/codeobject.c#L19
簡單地來講:
1.所有長度為0和1的字符串都會被駐留
2.字符串在編譯時被實現(xiàn)的會被駐留(如'wtf'會被駐留,但是 ''.join(['w', 't', 'f']) 不會)
3.字符串中只包含ASCII下的字母、數(shù)字和下劃線時會被駐留. 所以'wtf!'由于包含!不會被駐留。
我們的example1中,由于發(fā)生了駐留,所以a和b是同一個字符串對象。而example2中,由于沒有發(fā)生字符串駐留,a="wtf!"和b="wtf!"實際上使用的不是同一個字符串對象,你可以使用id獲得對象的唯一標志,你會發(fā)現(xiàn)它們的不同:
a和b都為wtf!時:
>>> a ="wtf!"
>>> b ="wtf!"
>>> a is b
False
>>> a == b
True
>>> id(a)
2272774097864
>>> id(b)
2272774097024
再來看看沒有發(fā)生駐留時的情況,a和b都為wtf時:
# a和b都為wtf
>>> a ="wtf"
>>> b ="wtf"
>>> a is b
True
>>> a == b
True
>>> id(a)
2272774096744
>>> id(b)
2272774096744
明白了吧?如果你想從結果識別對象是否發(fā)生駐留,關鍵就看對象的唯一標志有沒有被改變。
不過,如example3所示,當你在同一行中將a和b都設置為 wtf! 的時候,Python解釋器會創(chuàng)建一個新的對象,然后同時引用第二個變量,這時候它兩的唯一標志id就是一樣的。(example3僅適用于python3.7以下,后面被改了)。
example4中,發(fā)生了常量折疊,這其實也是一種優(yōu)化技術。編譯時表達式 'a'*20 會被替換成 'aaaaaaaaaaaaaaaaaaaa' (不要數(shù)了,20個),不過只有長度小于20的字符串才會發(fā)生常量替換,這就是為什么 'a'*21并不等于 'aaaaaaaaaaaaaaaaaaaaa' (不要數(shù)了,21個) 。
好,感謝大家的閱讀,今天的.....等等,你以為這就結束了嗎?還有呢:
>>> a =10
>>> b =10
>>> a is b
True
>>> a =256
>>> b =256
>>> a is b
True
>>> a =257
>>> b =257
>>> a is b
False
這又是為啥啊?
請注意,Python中,對于整數(shù)對象,如果其值處于[-5,256]的閉區(qū)間內(nèi),則值相同的對象是同一個對象,否則為不同對象。我知道你想問,別問,問就是源碼本身就這么寫的。
(其實主要還是從性能方面考慮,-5到256這段數(shù)值被經(jīng)常使用,因此干脆設為同一個對象重復使用,避免分配空間—賦予類別—賦予初始值等一系列操作)。
-
字符串
+關注
關注
1文章
579瀏覽量
20549 -
編譯
+關注
關注
0文章
659瀏覽量
32899 -
python
+關注
關注
56文章
4797瀏覽量
84787 -
Examples
+關注
關注
0文章
2瀏覽量
1621
發(fā)布評論請先 登錄
相關推薦
評論