最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python中字典的緩存池

 更新時間:2022年05月11日 10:29:05   作者:??編程學習網(wǎng)????  
這篇文章主要介紹了Python中字典的緩存池,字典的緩存池采用數(shù)組實現(xiàn)的,并且容量也是80個,下文詳細介紹需要的小伙伴可以參考一下

前言:

我們知道字典里面有一個ma_keys和ma_values,其中ma_keys是一個指向PyDictKeysObject的指針,ma_values是一個指向PyObject *數(shù)組的二級指針。當哈希表為分離表時,鍵由ma_keys維護,值由ma_values維護;當哈希表為結合表時,鍵和值均由ma_keys維護。

那么當我們在銷毀一個PyDictObject時,也肯定是要先釋放ma_keys和ma_values。

如果是分離表,會將每個value的引用計數(shù)減1,然后釋放ma_values;再將每個key的引用計數(shù)減1,然后釋放ma_keys。最后再釋放PyDictObject本身。

如果是結合表,由于key、value都在ma_keys中,將每個key、value的引用計數(shù)減1之后,只需要再釋放ma_keys即可。最后再釋放PyDictObject本身。

整個過程還是很清晰的,只不過這里面遺漏了點什么東西,沒錯,就是緩存池。在介紹浮點數(shù)的時候,我們說不同的對象都有自己的緩存池,當然字典也不例外。并且除了PyDictObject之外,PyDictKeysObject也有相應的緩存池,畢竟它負責存儲具體的鍵值對。

那么下面我們就來研究一下這兩者的緩存池。

PyDictObject緩存池

字典的緩存池和列表的緩存池高度相似,都是采用數(shù)組實現(xiàn)的,并且容量也是80個。

#ifndef PyDict_MAXFREELIST
#define PyDict_MAXFREELIST 80
#endif
static PyDictObject *free_list[PyDict_MAXFEELIST];
static int numfree = 0;  //緩存池當前存儲的元素個數(shù)

開始時,這個緩存池什么也沒有,直到第一個PyDictObject對象被銷毀時,緩存池里面才開始接納被銷毀的PyDictObject對象。

static void
dict_dealloc(PyDictObject *mp)
{  
    //獲取ma_values指針
    PyObject **values = mp->ma_values;
    //獲取ma_keys指針
    PyDictKeysObject *keys = mp->ma_keys;
    Py_ssize_t i, n;

    //因為要被銷毀,所以讓GC不再跟蹤
    PyObject_GC_UnTrack(mp);
    //用于延遲釋放
    Py_TRASHCAN_SAFE_BEGIN(mp)
        
    //調整引用計數(shù)
    //如果values不為NULL,說明是分離表    
    if (values != NULL) {
    //將指向的value、key的引用計數(shù)減1
    //然后釋放ma_values和ma_keys
        if (values != empty_values) {
            for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) {
                Py_XDECREF(values[i]);
            }
            free_values(values);
        }
        DK_DECREF(keys);
    }
    //否則說明是結合表
    else if (keys != NULL) {
    //結合表的話,dk_refcnt一定是1
    //此時只需要釋放ma_keys,因為鍵值對全部由它來維護
    //在DK_DECREF里面,會將每個key、value的引用計數(shù)減1
    //然后釋放ma_keys
        assert(keys->dk_refcnt == 1);
        DK_DECREF(keys);
    }
    //將被銷毀的對象放到緩存池當中
    if (numfree < PyDict_MAXFREELIST && Py_TYPE(mp) == &PyDict_Type)
        free_list[numfree++] = mp;
    else
    //如果緩存池已滿,則將釋放內(nèi)存
        Py_TYPE(mp)->tp_free((PyObject *)mp);
    Py_TRASHCAN_SAFE_END(mp)
}

同理,當創(chuàng)建字典時,也會優(yōu)先從緩存池里面獲取。

static PyObject *
new_dict(PyDictKeysObject *keys, PyObject **values)
{
    //...
    if (numfree) {
        mp = free_list[--numfree];
    }
    //...
}

因此在緩存池的實現(xiàn)上,字典和列表有著很高的相似性。不僅都是由數(shù)組實現(xiàn),在銷毀的時候也都會放在數(shù)組的尾部,創(chuàng)建的時候也會從數(shù)組的尾部獲取。當然啦,因為這么做符合數(shù)組的特性,如果銷毀和創(chuàng)建都是在數(shù)組的頭部操作,那么時間復雜度就從O(1)變成了O(n)。

我們用Python來測試一下:

d1 = {k: 1 for k in "abcdef"}
d2 = {k: 1 for k in "abcdef"}
print("id(d1):", id(d1))
print("id(d2):", id(d2))
# 放到緩存池的尾部
del d1
del d2
# 緩存池:[d1, d2]

# 從緩存池的尾部獲取
# 顯然id(d3)和上面的id(d2)是相等的
d3 = {k: 1 for k in "abcdefghijk"}
# id(d4)和上面的id(d1)是相等的
d4 = {k: 1 for k in "abcdefghijk"}
print("id(d3):", id(d3))
print("id(d4):", id(d4))
# 輸出結果
"""
id(d1): 1363335780736
id(d2): 1363335780800
id(d3): 1363335780800
id(d4): 1363335780736
"""

輸出結果和我們的預期是相符合的,以上就是PyDictObject的緩存池。

PyDictKeysObject緩存池

PyDictKeysObject也有自己的緩存池,同樣基于數(shù)組實現(xiàn),大小是80。

//PyDictObject的緩存池叫 free_list
//PyDictKeysObject的緩存池叫 keys_free_list
//兩者不要搞混了
static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
static int numfreekeys = 0;  //緩存池當前存儲的元素個數(shù)

我們先來看看它的銷毀過程:

static void
free_keys_object(PyDictKeysObject *keys)
{
    //將每個entry的me_key、me_value的引用計數(shù)減1
    for (i = 0, n = keys->dk_nentries; i < n; i++) {
        Py_XDECREF(entries[i].me_key);
        Py_XDECREF(entries[i].me_value);
    }
#if PyDict_MAXFREELIST > 0
    //將其放在緩存池當中
    //當緩存池未滿、并且dk_size為8的時候被緩存
    if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) {
        keys_free_list[numfreekeys++] = keys;
        return;
    }
#endif
    PyObject_FREE(keys);
}

銷毀的時候,也是放在了緩存池的尾部,那么創(chuàng)建的時候肯定也是先從緩存池的尾部獲取。

static PyDictKeysObject *new_keys_object(Py_ssize_t size)
{
    PyDictKeysObject *dk;
    Py_ssize_t es, usable;
    //...
    //創(chuàng)建 ma_keys,如果緩存池有可用對象、并且size等于8,
    //那么會從 keys_free_list 中獲取
    if (size == PyDict_MINSIZE && numfreekeys > 0) {
        dk = keys_free_list[--numfreekeys];
    }
    else {
        // 否則malloc重新申請
        dk = PyObject_MALLOC(sizeof(PyDictKeysObject)
                             + es * size
                             + sizeof(PyDictKeyEntry) * usable);
        }
    }
    //...
    return dk;
}

所以PyDictKeysObject的緩存池和列表同樣是高度相似的,只不過它想要被緩存,還需要滿足一個額外的條件,那就是dk_size必須等于8。很明顯,這個限制是出于對內(nèi)存方面的考量。

我們還是來驗證一下:

import ctypes
class PyObject(ctypes.Structure):
    _fields_ = [("ob_refcnt", ctypes.c_ssize_t),
                ("ob_type", ctypes.c_void_p)]
class PyDictObject(PyObject):
    _fields_ = [("ma_used", ctypes.c_ssize_t),
                ("ma_version_tag", ctypes.c_uint64),
                ("ma_keys", ctypes.c_void_p),
                ("ma_values", ctypes.c_void_p)]
d1 = {_: 1 for _ in "mnuvwxyz12345"}
print(
    PyDictObject.from_address(id(d1)).ma_keys
)  # 1962690551536
# 鍵值對個數(shù)超過了8,dk_size必然也超過了 8
# 那么當銷毀d1的時候,d1.ma_keys不會被緩存
# 而是會直接釋放掉
del d1
d2 = {_: 1 for _ in "a"}
print(
    PyDictObject.from_address(id(d2)).ma_keys
)  # 1962387670624

# d2 的 dk_size 顯然等于 8
# 因此它的 ma_keys 是會被緩存的
del d2
d3 = {_: 1 for _ in "abcdefg"}
print(
    PyDictObject.from_address(id(d3)).ma_keys
)  # 1962699215808
# 盡管 d2 的 ma_keys 被緩存起來了
# 但是 d3 的 dk_size 大于 8
# 因此它不會從緩存池中獲取,而是重新創(chuàng)建
# d4 的 dk_size 等于 8
# 因此它會獲取 d2 被銷毀的 ma_keys
d4 = {_: 1 for _ in "abc"}
print(
    PyDictObject.from_address(id(d4)).ma_keys
)  # 1962387670624

所以從打印的結果來看,由于d4.ma_keys和d2.ma_keys是相同的,因此證實了我們的結論。不像列表和字典,它們是只要被銷毀,就會放到緩存池里面,因為它們沒有存儲具體的數(shù)據(jù),大小是固定的。

但是PyDictKeysObject不同,它存儲了entry,每個entry占24字節(jié)。如果內(nèi)部的entry非常多,那么緩存起來會有額外的內(nèi)存開銷。因此Python的策略是,只有在dk_size等于8的時候,才會緩存。當然這三者在緩存池的實現(xiàn)上,是基本一致的。

小結

總的來說,Python的字典是一個被高度優(yōu)化的數(shù)據(jù)結構,因為解釋器在運行的時候也重度依賴字典,這就決定了它的效率會非常高。當然,我們沒有涉及字典的全部內(nèi)容,比如字典有很多方法,比如keys、values、items方法等等,我們并沒有說。這些有興趣的話,可以對著源碼看一遍,不是很難??傊覀兤綍r,也可以盡量多使用字典。

到此這篇關于Python中字典的緩存池的文章就介紹到這了,更多相關Python緩存池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Union在Python類型注解中的應用與最佳實踐

    Union在Python類型注解中的應用與最佳實踐

    Union” 在中文中通常翻譯為“聯(lián)合”,在數(shù)學和邏輯學中,它指的是兩個或多個集合的并集,在 Python 的類型注解中,Union 類型表示一個變量可以是多種類型中的任意一種,這與數(shù)學中的并集概念相似,本文介紹了Union在Python類型注解中的應用與最佳實踐
    2024-09-09
  • Python 多線程,threading模塊,創(chuàng)建子線程的兩種方式示例

    Python 多線程,threading模塊,創(chuàng)建子線程的兩種方式示例

    這篇文章主要介紹了Python 多線程,threading模塊,創(chuàng)建子線程的兩種方式,結合實例形式分析了Python線程的原理與創(chuàng)建子線程的相關實現(xiàn)技巧,需要的朋友可以參考下
    2019-09-09
  • python將文本轉換成圖片輸出的方法

    python將文本轉換成圖片輸出的方法

    這篇文章主要介紹了python將文本轉換成圖片輸出的方法,涉及Python操作文本及圖片的相關技巧,非常具有實用價值,需要的朋友可以參考下
    2015-04-04
  • 淺談python 中類屬性共享的問題

    淺談python 中類屬性共享的問題

    今天小編就為大家分享一篇淺談python 中類屬性共享的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • Python time庫基本使用方法分析

    Python time庫基本使用方法分析

    這篇文章主要介紹了Python time庫基本使用方法,結合實例形式分析了Python time模塊基本功能、控制符、使用方法與操作注意事項,需要的朋友可以參考下
    2019-12-12
  • Python實現(xiàn)將羅馬數(shù)字轉換成普通阿拉伯數(shù)字的方法

    Python實現(xiàn)將羅馬數(shù)字轉換成普通阿拉伯數(shù)字的方法

    這篇文章主要介紹了Python實現(xiàn)將羅馬數(shù)字轉換成普通阿拉伯數(shù)字的方法,簡單分析了羅馬數(shù)字的構成并結合實例形式給出了Python轉換羅馬數(shù)字為阿拉伯數(shù)字的實現(xiàn)方法,需要的朋友可以參考下
    2017-04-04
  • pandas DataFrame實現(xiàn)幾列數(shù)據(jù)合并成為新的一列方法

    pandas DataFrame實現(xiàn)幾列數(shù)據(jù)合并成為新的一列方法

    今天小編就為大家分享一篇pandas DataFrame實現(xiàn)幾列數(shù)據(jù)合并成為新的一列方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06
  • Python 中的 else詳解

    Python 中的 else詳解

    這篇文章主要介紹了Python 中的 else詳解的相關資料,需要的朋友可以參考下
    2016-04-04
  • Pandas 模糊查詢與替換的操作

    Pandas 模糊查詢與替換的操作

    這篇文章主要介紹了Pandas 模糊查詢與替換的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • 關于PyCharm安裝后修改路徑名稱使其可重新打開的問題

    關于PyCharm安裝后修改路徑名稱使其可重新打開的問題

    這篇文章主要介紹了關于PyCharm安裝后修改路徑名稱使其可重新打開的問題,本文通過圖文實例相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10

最新評論

西乌| 延津县| 临邑县| 泸溪县| 平乡县| 牙克石市| 屏东县| 沾益县| 涟水县| 长顺县| 鄄城县| 会同县| 贵州省| 丹寨县| 古浪县| 浦县| 丰宁| 锡林浩特市| 阿合奇县| 南澳县| 白水县| 石河子市| 高青县| 黄平县| 五指山市| 太原市| 阳高县| 秀山| 左云县| 达州市| 光山县| 平遥县| 彩票| 聂拉木县| 临泽县| 衡南县| 阳高县| 堆龙德庆县| 洛扎县| 雷州市| 独山县|