亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

為什么Python 3.6以后字典有序并且效率更高?

系統 2147 0

在Python 3.5(含)以前,字典是不能保證順序的,鍵值對A先插入字典,鍵值對B后插入字典,但是當你打印字典的Keys列表時,你會發現B可能在A的前面。

但是從Python 3.6開始,字典是變成有順序的了。你先插入鍵值對A,后插入鍵值對B,那么當你打印Keys列表的時候,你就會發現B在A的后面。

不僅如此,從Python 3.6開始,下面的三種遍歷操作,效率要高于Python 3.5之前:

          for key in 字典
          

for value in 字典.values()

for key, value in 字典.items()

從Python 3.6開始,字典占用內存空間的大小,視字典里面鍵值對的個數,只有原來的30%~95%。

Python 3.6到底對字典做了什么優化呢?為了說明這個問題,我們需要先來說一說,在Python 3.5(含)之前,字典的底層原理。

當我們初始化一個空字典的時候,CPython的底層會初始化一個二維數組,這個數組有8行,3列,如下面的示意圖所示:

          my_dict = {}
          

'''
此時的內存示意圖
[[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[---, ---, ---]]
'''

現在,我們往字典里面添加一個數據:

          my_dict['name'] = 'kingname'
          

'''
此時的內存示意圖
[[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[1278649844881305901, 指向name的指針, 指向kingname的指針],
[---, ---, ---],
[---, ---, ---]]
'''

這里解釋一下,為什么添加了一個鍵值對以后,內存變成了這個樣子:

首先我們調用Python 的 hash 函數,計算 name 這個字符串在 當前運行時 的hash值:

          >>> hash('name')
          
1278649844881305901

特別注意,我這里強調了『當前運行時』,這是因為,Python自帶的這個 hash 函數,和我們傳統上認為的Hash函數是不一樣的。Python自帶的這個 hash 函數計算出來的值,只能保證在每一個運行時的時候不變,但是當你關閉Python再重新打開,那么它的值就可能會改變,如下圖所示:

image

假設在某一個運行時里面, hash('name') 的值為 1278649844881305901 ?,F在我們要把這個數對8取余數:

          >>> 1278649844881305901 % 8
          
5

余數為5,那么就把它放在剛剛初始化的二維數組中,下標為5的這一行。由于 name kingname 是兩個字符串,所以底層C語言會使用兩個字符串變量存放這兩個值,然后得到他們對應的指針。于是,我們這個二維數組下標為5的這一行,第一個值為 name 的hash值,第二個值為 name 這個字符串所在的內存的地址(指針就是內存地址),第三個值為 kingname 這個字符串所在的內存的地址。

現在,我們再來插入兩個鍵值對:

          my_dict['age'] = 26
          
my_dict['salary'] = 999999

'''
此時的內存示意圖
[[-4234469173262486640, 指向salary的指針, 指向999999的指針],
[1545085610920597121, 執行age的指針, 指向26的指針],
[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[1278649844881305901, 指向name的指針, 指向kingname的指針],
[---, ---, ---],
[---, ---, ---]]
'''

那么字典怎么讀取數據呢?首先假設我們要讀取 age 對應的值。

此時,Python先計算在當前運行時下面, age 對應的Hash值是多少:

          >>> hash('age')
          
1545085610920597121

現在這個hash值對8取余數:

          >>> 1545085610920597121 % 8
          
1

余數為1,那么二維數組里面,下標為1的這一行就是需要的鍵值對。直接返回這一行第三個指針對應的內存中的值,就是 age 對應的值 26

當你要循環遍歷字典的Key的時候,Python底層會遍歷這個二維數組,如果當前行有數據,那么就返回Key指針對應的內存里面的值。如果當前行沒有數據,那么就跳過。所以總是會遍歷整個二位數組的每一行。

每一行有三列,每一列占用8byte的內存空間,所以每一行會占用24byte的內存空間。

由于Hash值取余數以后,余數可大可小,所以字典的Key并不是按照插入的順序存放的。

注意,這里我省略了與本文沒有太大關系的兩個點:

          開放尋址
          

在Python 3.6以后,字典的底層數據結構發生了變化,現在當你初始化一個空的字典以后,它在底層是這樣的:

          my_dict = {}
          

'''
此時的內存示意圖
indices = [None, None, None, None, None, None, None, None]

entries = []
'''

當你初始化一個字典以后,Python單獨生成了一個長度為8的一維數組。然后又生成了一個空的二維數組。

現在,我們往字典里面添加一個鍵值對:

          my_dict['name'] = 'kingname'
          

'''
此時的內存示意圖
indices = [None, 0, None, None, None, None, None, None]

entries = [[-5954193068542476671, 指向name的指針, 執行kingname的指針]]
'''

為什么內存會變成這個樣子呢?我們來一步一步地看:

在當前運行時, name 這個字符串的hash值為 -5954193068542476671 ,這個值對8取余數是1:

          >>> hash('name')
          
-5954193068542476671

hash('name') % 8
1

所以,我們把 indices 這個一維數組里面,下標為1的位置修改為0。

這里的0是什么意思呢?0是二位數組 entries 的索引?,F在 entries 里面只有一行,就是我們剛剛添加的這個鍵值對的三個數據: name 的hash值、指向 name 的指針和指向 kinganme 的指針。所以 indices 里面填寫的數字0,就是剛剛我們插入的這個鍵值對的數據在二位數組里面的行索引。

好,現在我們再來插入兩條數據:

            my_dict['address'] = 'xxx'
            
my_dict['salary'] = 999999

'''
此時的內存示意圖
indices = [1, 0, None, None, None, None, 2, None]

entries = [[-5954193068542476671, 指向name的指針, 執行kingname的指針],
[9043074951938101872, 指向address的指針,指向xxx的指針],
[7324055671294268046, 指向salary的指針, 指向999999的指針]
]
'''

現在如果我要讀取數據怎么辦呢?假如我要讀取 salary 的值,那么首先計算 salary 的hash值,以及這個值對8的余數:

            >>> hash('salary')
            
7324055671294268046

hash('salary') % 8
6

那么我就去讀 indices 下標為6的這個值。這個值為2.

然后再去讀entries里面,下標為2的這一行的數據,也就是salary對應的數據了。

新的這種方式,當我要插入新的數據的時候,始終只是往 entries 的后面添加數據,這樣就能保證插入的順序。當我們要遍歷字典的Keys和Values的時候,直接遍歷 entries 即可,里面每一行都是有用的數據,不存在跳過的情況,減少了遍歷的個數。

最后在此推薦小編創建的Python學習交流群:835017344,這里是python學習者聚集地,有大牛答疑,有資源共享!有想學習python編程的,或是轉行,或是大學生,還有工作中想提升自己能力的,正在學習的小伙伴歡迎加入學習。

老的方式,當二維數組有8行的時候,即使有效數據只有3行,但它占用的內存空間還是 8 * 24 = 192 byte。但使用新的方式,如果只有三行有效數據,那么 entries 也就只有3行,占用的空間為3 * 24 =72 byte,而 indices 由于只是一個一維的數組,只占用8 byte,所以一共占用 80 byte。內存占用只有原來的41%。


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 欧美区一区二 | 中文字幕在线视频在线看 | 三a大片| 97色在线播放| 久久国产影视免费精品 | 香蕉精品高清在线观看视频 | 九九精品国产 | 久久综合九九亚洲一区 | 99热国内精品 | 国产欧美在线观看精品一区二区 | 亚洲精品一区二区三区在线播放 | 男女一级毛片免费视频看 | 91亚洲国产三上悠亚在线 | 四虎+网站+影院+网站 | 天天做夜夜做 | 97福利视频 | 伊人久久婷婷丁香六月综合基地 | 成人精品一级毛片 | 亚州综合| 国产成人永久免费视 | 操美女免费看 | 国产极品嫩模在线观看91精品 | 国产性大片黄在线观看在线放 | 精品日韩一区二区 | 99婷婷| 久久精品综合国产二区 | 奇米影视奇米色777欧美 | 国产1000部成人免费视频 | 亚洲精品一区二区久久久久 | 亚洲综合色站 | 久草看片| 色综合久久九月婷婷色综合 | 国内精品久久久久鸭 | 国产精品久久久久久搜索 | 伊人婷婷色香五月综合缴缴情 | 美女bbxx美女bbb| 亚洲国产成人九九综合 | 三级a做爰大乳在线观看 | 四虎国产精品永久地址49 | 国产精品亚洲二区在线 | 亚洲视频第二页 |