1. 程式人生 > >Python原始碼剖析-Dict

Python原始碼剖析-Dict

為了刻畫某種關係,現代的程式語言都會提供關聯式的容器。關聯式容器中的元素分別是以(鍵(key)或值(value))這樣的形式存在。例如(3,5)(3,6)就是一對對應的鍵與值。

Python中的關聯式容器是PyDictObject。Python通過PyDictObject建立執行Python位元組碼的執行環境,其中會存放變數名和變數值的元素對,通過查詢變數名獲得變數值。

PyDictObject運用了(hash table)散列表,最優情況下能達到O(1)複雜度的搜尋效率。

散列表:


基本思想:鍵值對映整數,通過一片連續記憶體的索引對應相對的值

雜湊效率高,但是不同的物件運用雜湊函式有可能會產生相同的雜湊值、即是易衝突

相關概念:裝載率(如果散列表一共可以容納10個元素,而當前已經裝入6個元素,那麼裝載率就是6/10),研究表明當散列表的裝載率大於2/3時,雜湊衝突的發生概率大大增加

解決辦法:開放定址法(通過一個二次探測函式f,計算下一個地址,如果可用則插入不可用則計算下一個候選位置)

缺點:刪除時不能完成刪除元素,否則會造成斷鏈,解決的辦法是:偽刪除

entry/slot ->關聯容器(鍵)

一個entry的定義如下:

typedef struct {  

Py_ssize_t me_hash;  //記錄me_key 的雜湊值,避免每次都要計算

PyObject *me_key;   //指向鍵

PyObject *me_value; //指向值

} PyDictEntry;

因為key和value 都是PyObject,故什麼東西都可以放進去Dict entry有三種狀態: ①Unused態 →key,value = Null ②Active 態 → key!= dummy,key!=Null,value != Null ③Dummy 態 → key = Dummy, value = Null 三態的轉換關係如下:

在PyDictObject定義中, 有一個ma_smalltable(PyDict_MINSIZE) 意味著至少有X個entry被建立(在這裡X=8, 可在原始碼修改) 定義是分兩種情況:①元素數目小於8, ma_table 指向 ma_smalltable  ②元素數目大於8, ma_table指向額外記憶體 第一次呼叫PyNew的時候會建立Dummy物件,Dummy則指向一個PyStringObject物件 元素的搜尋:lookdict、lookdict_string-(演算法相同)  後者是為鍵值為String提供便利 因為在python中一PyStringObject物件作為PyDictObject的鍵是十分常見的,故lookdict_string成為了PyDictObject中預設的搜尋演算法 dict的key匹配值有兩層含義: ①引用相同,即兩符號指向同一地址 ②值相同,兩個物件指向不同的地址,但是值相同 e.g.:Python大整數是不共享記憶體的,如下例子:
d = {}
d[9527] = 'Python'
print(d[9527]) 
//上面兩個9527指向不同的地址,但值相同

接下來看看第一次搜尋: [1]根據hash獲得entry的索引,這裡是第一個索引 [2]if entry處於Unused態,即字典中無這個key,第一個索引失敗 if entry->me_key ==  key,表明匹配,搜尋成功 [3]if entry 處於Dummy態,設定freeslot(最後如果找不到是會返回freeslot,提示系統這裡有一個dummy,快來用掉它) [4]if entry 處於active ,檢查是否值相同,若相同則搜尋成功 所第一個entry不匹配,則沿著探測鏈,順藤摸瓜,依次比較探測鏈上的entry與帶查詢的key ①若搜尋成功,則ep一定指向一個有效的entry ②若搜尋不成功,此時ep肯定處於一個Unused態的entry 不能直接返回,因為有可能在搜尋鏈的過程中發現dummy,故須返回給Python使用 lookdict_string(有條件限制)(優化) key是PyDictObject
if(!PyString_checkEcact(key)){
    mp->ma_lookup = lookdict;
    return lookdict(mp_key, hash);
}

lookdict_string 效率比lookdict高 元素插入: ep = mp->ma_lookup(mp, key, hash); 成功:
if (ep->me_value != Null){
old_value = ep->me_value;
ep->me_value = new_value;
Py_DECREF(old_value);
Py_DECREF(key);
}
else將key, value覆給ep(ep可以為Dummy,Unused) 在插入元素的動作結束後,會檢查是否需要改變PyDictObject內部ma_table的記憶體大小 條件:當增加元素個數且裝載率大於2/3時 改變記憶體大小由dictresize執行 [1]確定新的table大小,這個大小要大於傳入的數值,從8開始乘以2的指數倍(2的n此方),直到超過傳入的數值 [2]==8,不需要重新分配記憶體,直接指向ma_smalltable [3]>=8,重新分配記憶體 [5]對非Unused做處理,若為active則插入,若為dummy則丟棄,也就是重新做字典,類似一個新字典裡面是沒有dummy的 [6]如果舊的table指向記憶體(>8)則釋放 刪除:與插入很像,先計算hash值,找到相對應的entry,刪除維護的元素,將active轉為dummy,調整table使用情況(ma_used--1) 字典結束

相關推薦

Python原始碼剖析-Dict

為了刻畫某種關係,現代的程式語言都會提供關聯式的容器。關聯式容器中的元素分別是以(鍵(key)或值(value))這樣的形式存在。例如(3,5)(3,6)就是一對對應的鍵與值。 Python中的關聯式容器是PyDictObject。Python通過PyDictObject建

Python 原始碼剖析(一)【python物件】

處於研究python記憶體釋放問題,在閱讀部分python原始碼,順便記錄下所得。 (基於《python原始碼剖析》(v2.4.1)與 python原始碼(v2.7.6)) 先列下總結:         python 中一切皆為物件,所以會先講明白pyth

Python原始碼剖析----第二章

第二章  Python的整數物件 2.1 基本概念 在Python中,整數物件是不可變物件,即建立一個PyIntObject物件之後,就再也不能改變該物件所維護的那個真實的整數值了。但在實際Python的應用程式中,整數的使用太過廣泛,為避免頻繁建立,Python為整數物件

Python 原始碼剖析》一些理解以及勘誤筆記(3)

以下是本人閱讀此書時理解的一些筆記,包含一些影響文義的筆誤修正,當然不一定正確,貼出來一起討論。 注:此書剖析的原始碼是2.5版本,在python.org 可以找到原始碼。紙質書閱讀,pdf 貼圖。 文章篇幅太長,故切分成3部分,這是第三部分。 p316: 初始化

Python原始碼剖析[1] —— 編譯Python

在中間的部分,可以看到Python的核心,直譯器(interpreter)。在直譯器中,箭頭的方向指示了Python執行時的資料流方向。其中Scanner對應詞法分析,將檔案輸入的Python原始碼或從命令列輸入的一行行Python程式碼切分為一個一個的token;Parser對應語法分析部分,在Scanne

Python原始碼剖析----第一章

第一章 1.1 Python的內建物件 在python中,物件就是為C中的結構體在堆上申請的一塊記憶體,一般不能被靜態初始化 但是,型別物件是唯一的例外,python中所有的內建的型別物件都是被靜態初始化的。 Python中的內建物件的頭結構都是對Pyobject結構的擴充

Python原始碼剖析】物件模型概述

*Python* 是一門 **面向物件** 語言,實現了一個完整的面向物件體系,簡潔而優雅。 與其他面向物件程式語言相比, *Python* 有自己獨特的一面。 這讓很多開發人員在學習 *Python* 時,多少有些無所適從。 那麼,*P

python重試庫retryiny原始碼剖析

  上篇博文介紹了常見需要進行請求重試的場景,本篇博文試著剖析有名的python第三方庫retrying原始碼。    在剖析其原始碼之前,有必要講一下retrying的用法,方便理解。    安裝:   pip install retryin

Python envoy 模組原始碼剖析

Kenneth Reitz 是公認的這個世界上 Python 程式碼寫得最好的人之一。抱著學習的心態,我閱讀了 Reitz 寫的 envoy 模組的原始碼,將筆記記錄如下。 介紹 和 requests 模組一樣,envoy 也是 Reitz 的作品,連官方描述都類

一個Python開源專案-騰訊哈勃沙箱原始碼剖析(上)

前言 2019年來了,2020年還會遠嗎? 請把下一年的年終獎發一下,謝謝。。。 回顧逝去的2018年,最大的改變是從一名學生變成了一位工作者,不敢說自己多麼的職業化,但是正在努力往那個方向走。 以前想的更多是嘗試,現在需要考慮的更多是落地。學校和公司還是有很大的不一樣,學到了很多東西。  

一個Python開源專案-哈勃沙箱原始碼剖析(下)

前言 在上一篇中,我們講解了哈勃沙箱的技術點,詳細分析了靜態檢測和動態檢測的流程。本篇接著對動態檢測的關鍵技術點進行分析,包括strace,sysdig,volatility。volatility的介紹不會太深入,記憶體取證這部分的研究還需要繼續。 strace機制 上一篇講到了st

唯快不破:redis原始碼剖析03-dict雜湊表結構

精讀了一遍dict整個實現的原始碼,發現dict的實現程式碼還是比較優秀的,程式碼邏輯非常嚴謹。尤其是dict的擴充實現感覺比較好,dict每次擴充都是按照2的指數被擴充,在擴充的過程是循序漸進的,不是一下全部由老表遷移到新表,而是有個再hash的過程,會將以前的元素重新再

python str轉dict

eva 官方 字符 表達 pri 局限 prompt num pro 兩種方法 捷徑 eval(str) >>> user = "{‘name‘ : ‘jim‘, ‘sex‘ : ‘male‘, ‘age‘: 18}" >>> type(

Python Mysql Select Dict

range 日期轉換 als () timedelta 5-0 times pytho fetch Python Select Mysql 日期轉換字符串 ,轉換為字典 Python 讀取出來的數據格式不是正規Json ,讀取出來,直接是字典 Python

Python實戰之dict簡單練習

簡單 fault zhang zha contain default san rom mat [‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__

Python中的dict

python dict 字典 內置類型 hash key value# dict # Python內置了字典:dict的支持,dict全稱dictionary,在其他語言中也稱為map,使用鍵-值(key-value)存儲,具有極快的查找速度。 d = {‘Michael‘: 95, ‘Bob‘: 75,

Python學習之dict和set

不可變 需要 after 集合 value 報錯 list 也會 如果 #coding=utf-8 # dict dict= {‘bob‘: 40, ‘andy‘: 30} print dict[‘bob‘] # 通過dict提供的get方法,如果key不存在,可以返回N

python基礎之dict和set

算法 dict name 區別 刪除元素 哈希算法 div 數學 blog dict dict是dictionary的縮寫,python內置了字典,在其他語言中也稱為map,使用鍵值對儲存,具有極快的查找速度。 如果是只用list來實現,就需要兩個list,先在第一個lis

Python: TypeError: 'dict' object is not callable

color call error gpo -c typeerror python字典 type 問題 問題: TypeError: ‘dict‘ object is not callable 原因: dict()是python的一個內建函數,如果將dict自定義為一個p

Python list,dict問題解答

cal sub pri 回發 調用 port 一個 uil har 問題: 編寫一個函數 most_prolific,其將采用與上述 Beatles_Discography 示例相同的字典格式,並返回發布最多專輯的年份。如果在 Beatles_Discography 中調