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