聽說90%的人都答不對這道Python題

劉志軍發表於2017-11-18

每種程式語言都有一些不為人知的陷阱,有些實際工作中會踩到,有些可能根本排不上用場,但弄明白這些陷阱有利於我們更好的去了解這門語言的實現機制。

下面這個題,你是否能一眼看出問題的本質。

# 第一組
>>> a = 256
>>> b = 256
>>> a is b
True
# 第二組
>>> a = 257
>>> b = 257
>>> a is b
False
# 第三組
>>> a = 257; b = 257
>>> a is b
True複製程式碼

不管是 Python2 還是 Python3 環境下,只要你是在 CPython 的互動式命令列 REPL 中執行,結果沒什麼不同。

我們知道 is 比較的是兩個物件的記憶體地址是否一樣( id 函式返回一個和物件的記憶體地址相關的值),言外之意就是看a,b兩個變數是否指向同一個物件。我們來看看每個變數的 id 值。

>>> a = 256
>>> id(a)
1721788128
>>> b = 256
>>> id(a)
1721788128

>>> a = 257
>>> id(a)
14947024
>>> b = 257
>>> id(b)
14947104

>>> a = 257; b=257
>>> id(a)
14947136
>>> id(b)
14947136
>>>複製程式碼

不出所料,前後兩組 a,b的 id 值是相同的,只有中間這組 id 值不一樣,我們可以對其簡單分析一下原因。在 Python 中,一切皆為物件,理論上任意兩個物件的 id 值都是不一樣的,例如:

>>> nums = [1,2,3,4]
>>> id(nums)
15148936

>>> nums2 = [1,2,3]
>>> id(nums2)
15160824

>>> nums3 = [1,2,3]
>>> id(nums3)
15160864複製程式碼

看得出每個物件的 id 值是不同的,哪怕兩個物件的值(內容)相同,他們的 id 值也是不一樣的(nums2和nums3)。那為什麼前面第一組兩個物件的id值相同呢?可能有些同學已經知道了

因為在 Python 中,我們需要使用物件的時候 Python 就會為我們建立好,當不需要了它就會進行回收,就好比屋子裡面的東西用完之後,要及時清理,否則整個屋子很快就會堆滿,最終導致房間再也塞不進任何東西。

同樣的,為了提高效能,Python 就把一些常用的整數專門快取起來,就像屋子裡面有些東西總是每天都要頻繁使用,比如床,你不能說睡完之後,就把床搬出去,要用了再搬回來,這樣的效率太低,因為這個搬運過程實在是太耗時了。於是,我們可以專門拿一塊空間用來放置這個床。

Python 中也是同樣的道理,因為整數是我們經常使用的物件,為了避免重複的建立、回收,乾脆就把那些常用的整數快取起來,每次需要使用時直接從快取中拿,而不是重新建立(重新建立的話,肯定是一個全新的物件)。這些整數的範圍是[-5, 256],當然這個數字範圍是Python之父決定的,你要改,必須重新編譯Python環境。

現在我們就能解釋第一組為什麼是True,第二組為什麼是False了。為什麼第三組結果又是 True 了?,不是說好大於256的整數不再快取,每次使用都是新物件嗎?別急,再聽我囉嗦一下。

還是出於效能考慮,Python內部做了進一步優化,怎麼優化呢?但凡是在同一個程式碼塊中的程式碼,如果出現兩個值相同的整數,那麼它們將被重用,來看下面這個程式碼:

# test.py
# -*- coding: utf-8 -*-
a = 257
b = 257

def func():
    c = 257
    print(a is c)  # False

print(a is b)  # True

func()複製程式碼

上面程式碼是在一個 test.py 檔案中,執行時,a和b的id值相同,而c的id值與a不一樣,因為a、b 在同一個程式碼塊,屬於模組級別,而 c 是在函式裡面,屬於區域性變數,他們不屬於同一程式碼塊中,因此函式裡面的 257 這個物件時會重新建立,而建立 b 的時候,發現同級程式碼塊中有個257的值了,就重用了這個物件。

再回到前面講的第三組值,在 Python 的互動式命令列 REPL 中,每單獨一行都視為一個程式碼塊,同一行中的程式碼屬於同一個程式碼塊,因此不難理解,第三組中的a和b處在同一個程式碼塊中,所以後者重用了前者,因此,兩個變數的id是相同的。

有沒有覺得這是一個坑。雖然我們實際場景中並不一定能用上,但是至少我們知道了Python為我們做的一些優化工作。

部落格:foofish.net
公眾號:Python之禪

python之禪
python之禪

相關文章