欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python列表創(chuàng)建與銷毀及緩存池機制

 更新時間:2022年05月12日 10:21:33   作者:??編程學習網????  
這篇文章主要介紹了Python列表創(chuàng)建與銷毀及緩存池機制,文章基于python展開對列表創(chuàng)建與銷毀內容的展開,具有一定的參考價值,需要的小伙伴可以參考一下

列表的創(chuàng)建

創(chuàng)建列表,Python底層只提供了唯一一個Python/C API,也就是PyList_New。這個函數接收一個size參數,允許我們在創(chuàng)建一個PyListObject對象時指定底層的PyObject *數組的長度。

PyObject *
PyList_New(Py_ssize_t size)
{  
    //聲明一個PyListObject *對象
    PyListObject *op;
#ifdef SHOW_ALLOC_COUNT
    static int initialized = 0;
    if (!initialized) {
        Py_AtExit(show_alloc);
        initialized = 1;
    }
#endif
  
    //如果size小于0,直接拋異常
    if (size < 0) {
        PyErr_BadInternalCall();
        return NULL;
    }
    //緩存池是否可用,如果可用
    if (numfree) {
        //將緩存池內對象個數減1
        numfree--;
        //從緩存池中獲取
        op = free_list[numfree];
        //設置引用計數
        _Py_NewReference((PyObject *)op);
#ifdef SHOW_ALLOC_COUNT
        count_reuse++;
#endif
    } else {
        //不可用的時候,申請內存
        op = PyObject_GC_New(PyListObject, &PyList_Type);
        if (op == NULL)
            return NULL;
#ifdef SHOW_ALLOC_COUNT
        count_alloc++;
#endif
    }
    //如果size等于0,ob_item設置為NULL
    if (size <= 0)
        op->ob_item = NULL;
    else {
        //否則的話,創(chuàng)建一個指定容量的指針數組,然后讓ob_item指向它
        //所以是先創(chuàng)建PyListObject對象, 然后創(chuàng)建指針數組
        //最后通過ob_item建立聯(lián)系
        op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));
        if (op->ob_item == NULL) {
            Py_DECREF(op);
            return PyErr_NoMemory();
        }
    }
    //設置ob_size和allocated,然后返回op
    Py_SIZE(op) = size;
    op->allocated = size;
    _PyObject_GC_TRACK(op);
    return (PyObject *) op;
}

我們注意到源碼里面有一個緩存池,是的,Python大部分對象都有自己的緩存池,只不過實現(xiàn)的方式不同。

列表的銷毀

創(chuàng)建PyListObject對象時,會先檢測緩存池free_list里面是否有可用的對象,有的話直接拿來用,否則通過malloc在系統(tǒng)堆上申請。列表的緩存池是使用數組實現(xiàn)的,里面最多維護80個PyListObject對象。

#ifndef PyList_MAXFREELIST
#define PyList_MAXFREELIST 80
#endif
static PyListObject *free_list[PyList_MAXFREELIST];

根據之前的經驗我們知道,既然創(chuàng)建的時候能從緩存池中獲取,那么在執(zhí)行析構函數的時候也要把列表放到緩存池里面。

static void
list_dealloc(PyListObject *op)
{
    Py_ssize_t i;
    PyObject_GC_UnTrack(op);
    Py_TRASHCAN_SAFE_BEGIN(op)
    //先釋放底層數組
    if (op->ob_item != NULL) {
        i = Py_SIZE(op);
        //但是釋放之前,還有一件重要的事情
        //要將底層數組中每個指針指向的對象的引用計數都減去1
        //因為它們不再持有對"對象"的引用
        while (--i >= 0) {
            Py_XDECREF(op->ob_item[i]);
        }
        //然后釋放底層數組所占的內存
        PyMem_FREE(op->ob_item);
    }
    //判斷緩沖池里面PyListObject對象的個數,如果沒滿,就添加到緩存池
    //注意:我們看到執(zhí)行到這一步的時候, 底層數組已經被釋放掉了
    if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op))
    //添加到緩存池的時候,是添加到尾部
    //獲取的時候也是從尾部獲取
        free_list[numfree++] = op;
    else
        //否則的話就釋放掉PyListObject對象所占的內存
        Py_TYPE(op)->tp_free((PyObject *)op);
    Py_TRASHCAN_SAFE_END(op)
}

我們知道在創(chuàng)建一個新的PyListObject對象時,實際上是分為兩步的,先創(chuàng)建PyListObject對象,然后創(chuàng)建底層數組,最后讓PyListObject對象中的ob_item成員指向這個底層數組。

同理,在銷毀一個PyListObject對象時,先銷毀ob_item維護的底層數組,然后再釋放PyListObject對象自身(如果緩存池已滿)。

現(xiàn)在可以很清晰地明白了,原本空蕩蕩的緩存池其實是被已經死去的PyListObject對象填充了。在以后創(chuàng)建新的PyListObject對象時,Python會首先喚醒這些死去的PyListObject對象,給它們一個洗心革面、重新做人的機會。但需要注意的是,這里緩存的僅僅是PyListObject對象,對于底層數組,其ob_item已經不再指向了。

從list_dealloc中我們看到,PyListObject對象在放進緩存池之前,ob_item指向的數組就已經被釋放掉了,同時數組中指針指向的對象的引用計數會減1。所以最終數組中這些指針指向的對象也大難臨頭各自飛了,或生存、或毀滅,總之此時和PyListObject之間已經沒有任何聯(lián)系了。

但是為什么要這么做呢?為什么不連底層數組也一起維護呢?可以想一下,如果繼續(xù)維護的話,數組中指針指向的對象永遠不會被釋放,那么很可能會產生懸空指針的問題,所以這些指針指向的對象所占的空間必須交還給系統(tǒng)(前提是沒有其它指針指向了)。

但是實際上,是可以將PyListObject對象維護的底層數組進行保留的,即:只將數組中指針指向的對象的引用計數減1,然后將數組中的指針都設置為NULL,不再指向之前的對象了,但是并不釋放底層數組本身所占用的內存空間。

因此這樣一來,釋放的內存不會交給系統(tǒng)堆,那么再次分配的時候,速度會快很多。但是這樣帶來一個問題,就是這些內存沒人用也會一直占著,并且只能供PyListObject對象的ob_item指向的底層數組使用。因此Python還是為避免消耗過多內存,采取將底層數組所占的內存交還給了系統(tǒng)堆這樣的做法,在時間和空間上選擇了空間。

lst1 = [1, 2, 3]
print(id(lst1))  # 1243303086208
# 扔到緩存池中,放在數組的尾部
del lst1
# 從緩存池中獲取,也會從數組的尾部開始拿
lst2 = [1, 2, 3]
print(id(lst2))  # 1243303086208
# 因此打印的地址是一樣的

小結

作為一個功能強大的數據結構,多花些時間是有必要的。

到此這篇關于Python列表創(chuàng)建與銷毀及緩存池機制的文章就介紹到這了,更多相關Python 列表內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • python爬蟲超時的處理的實例

    python爬蟲超時的處理的實例

    今天小編就為大家分享一篇python爬蟲超時的處理的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-12-12
  • Pytorch 實現(xiàn)focal_loss 多類別和二分類示例

    Pytorch 實現(xiàn)focal_loss 多類別和二分類示例

    今天小編就為大家分享一篇Pytorch 實現(xiàn)focal_loss 多類別和二分類示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-01-01
  • nditer—numpy.ndarray 多維數組的迭代操作

    nditer—numpy.ndarray 多維數組的迭代操作

    這篇文章主要介紹了nditer—numpy.ndarray 多維數組的迭代操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Python?OpenCV實現(xiàn)姿態(tài)識別的詳細代碼

    Python?OpenCV實現(xiàn)姿態(tài)識別的詳細代碼

    這篇文章主要介紹了Python?OpenCV實現(xiàn)姿態(tài)識別的方法,本文通過截圖實例代碼相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • python 實現(xiàn)在shell窗口中編寫print不向屏幕輸出

    python 實現(xiàn)在shell窗口中編寫print不向屏幕輸出

    這篇文章主要介紹了python 實現(xiàn)在shell窗口中編寫print不向屏幕輸出的代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-02-02
  • python pandas移動窗口函數rolling的用法

    python pandas移動窗口函數rolling的用法

    今天小編就為大家分享一篇python pandas移動窗口函數rolling的用法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-02-02
  • 如何基于python3和Vue實現(xiàn)AES數據加密

    如何基于python3和Vue實現(xiàn)AES數據加密

    這篇文章主要介紹了如何基于python3和Vue實現(xiàn)AES數據加密,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • django ajax json的實例代碼

    django ajax json的實例代碼

    今天就為大家分享一篇django ajax json的實例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • Django實現(xiàn)支付寶付款和微信支付的示例代碼

    Django實現(xiàn)支付寶付款和微信支付的示例代碼

    支付寶支付和微信支付是當今互聯(lián)網產品常用的功能,這篇文章主要介紹了Django實現(xiàn)支付寶付款和微信支付的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • Python try except finally資源回收的實現(xiàn)

    Python try except finally資源回收的實現(xiàn)

    這篇文章主要介紹了Python try except finally資源回收的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01

最新評論