[英]Why does '() is ()' return True when '[] is []' and '{} is {}' return False?
據我所知,使用[], {}
或()
實例化對象分別返回list, dict
或tuple
的新實例; 具有新標識的新實例對象。
這對我來說很清楚,直到我實際測試它並且我注意到() is ()
實際上返回True
而不是預期的False
:
>>> () is (), [] is [], {} is {}
(True, False, False)
正如預期的那樣,當分別使用list()
、 dict()
和tuple()
創建對象時,也會表現出這種行為:
>>> tuple() is tuple(), list() is list(), dict() is dict()
(True, False, False)
我可以在tuple()
的文檔中找到的唯一相關信息是:
[...] 例如,
tuple('abc')
返回('a', 'b', 'c')
和tuple([1, 2, 3])
返回(1, 2, 3)
。 如果沒有給出參數,構造函數會創建一個新的空元組()
。
我只想說,這不足以回答我的問題。
那么,為什么空元組具有相同的身份,而其他人如列表或字典則沒有?
Python 在內部創建了一個元組對象的C
列表,其第一個元素包含空元組。 每次使用tuple()
或()
,Python 都會返回包含在上述C
列表中的現有對象,而不是創建一個新對象。
dict
或list
對象不存在這種機制,相反,它們每次都從頭開始重新創建。
這很可能與不可變對象(如元組)無法更改的事實有關,因此保證在執行期間不會更改。 當考慮到frozenset() is frozenset()
返回True
時,這進一步鞏固了; like ()
在CPython
的實現中,空的frozenset
被認為是單例。 對於可變對象,沒有這樣的保證,因此,沒有動力緩存它們的零元素實例(即,它們的內容可能會隨着身份保持不變而改變)。
請注意:這不是人們應該依賴的東西,即不應將空元組視為單例。 文檔中沒有明確做出這樣的保證,所以應該假設它是依賴於實現的。
在最常見的情況下, CPython
的實現是使用兩個宏PyTuple_MAXFREELIST
和PyTuple_MAXSAVESIZE
編譯為正整數。 這些宏的正值導致創建大小為PyTuple_MAXSAVESIZE
的tuple
對象數組。
當使用參數size == 0
調用PyTuple_New
,它確保將一個新的空元組添加到列表中(如果它尚不存在):
if (size == 0) {
free_list[0] = op;
++numfree[0];
Py_INCREF(op); /* extra INCREF so that this is never freed */
}
然后,如果請求一個新的空元組,將返回位於此列表第一個位置的元組而不是新實例:
if (size == 0 && free_list[0]) {
op = free_list[0];
Py_INCREF(op);
/* rest snipped for brevity.. */
促使這樣做的另一個原因是函數調用構造了一個元組來保存將要使用的位置參數。 這可以在ceval.c
的load_args
函數中ceval.c
:
static PyObject *
load_args(PyObject ***pp_stack, int na)
{
PyObject *args = PyTuple_New(na);
/* rest snipped for brevity.. */
在同一個文件中通過do_call
調用。 如果參數na
的數量為零,則將返回一個空元組。
本質上,這可能是一個經常執行的操作,因此不要每次都重建一個空元組是有意義的。
還有幾個答案闡明了CPython
的不可變緩存行為:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.