簡體   English   中英

當 '[] is []' 和 '{} is {}' 返回 False 時,為什么 '() is ()' 返回 True?

[英]Why does '() is ()' return True when '[] is []' and '{} is {}' return False?

據我所知,使用[], {}()實例化對象分別返回list, dicttuple的新實例; 具有新標識的新實例對象。

這對我來說很清楚,直到我實際測試它並且我注意到() 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列表中的現有對象,而不是創建一個新對象。

dictlist對象不存在這種機制,相反,它們每次都從頭開始重新創建

這很可能與不可變對象(如元組)無法更改的事實有關,因此保證在執行期間不會更改。 當考慮到frozenset() is frozenset()返回True時,這進一步鞏固了; like () CPython的實現中,空的frozenset 被認為是單例 對於可變對象,沒有這樣的保證,因此,沒有動力緩存它們的零元素實例(即,它們的內容可能會隨着身份保持不變而改變)。

請注意:這不是人們應該依賴的東西,即不應將空元組視為單例。 文檔中沒有明確做出這樣的保證,所以應該假設它是依賴於實現的。


它是如何完成的:

在最常見的情況下, CPython的實現是使用兩個宏PyTuple_MAXFREELISTPyTuple_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.cload_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM