自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入Python列表的內(nèi)部實現(xiàn)

開發(fā) 后端
本文將介紹列表在 CPython中的實現(xiàn),因為畢竟Cpython 又是 Python 最為常用的實現(xiàn)。

本文將介紹列表在 CPython中的實現(xiàn),因為畢竟Cpython 又是 Python 最為常用的實現(xiàn)。

Python 中的列表非常強大,看看它的內(nèi)部實現(xiàn)機制是怎么樣的,一定非常有趣。

下面是一段 Python 腳本,在列表中添加幾個整數(shù),然后打印列表。

  1. >>> l = [] 
  2.  
  3. >>> l.append(1) 
  4.  
  5. >>> l.append(2) 
  6.  
  7. >>> l.append(3) 
  8.  
  9. >>> l 
  10.  
  11. [1, 2, 3] 
  12.  
  13. >>> for e in l: 
  14.  
  15. ...   print e 
  16.  
  17. ... 
  18.  
  19.  
  20.  
  21.  

可以發(fā)現(xiàn),列表是一個迭代器。

列表對象的 C 語言結(jié)構(gòu)體

Cpython 中的列表實現(xiàn)類似于下面的 C 結(jié)構(gòu)體。ob_item 是指向列表對象的指針數(shù)組。allocated 是申請內(nèi)存的槽的個數(shù)。

  1. typedef struct { 
  2.  
  3.     PyObject_VAR_HEAD 
  4.  
  5.     PyObject **ob_item; 
  6.  
  7.     Py_ssize_t allocated; 
  8.  
  9. } PyListObject;  

列表初始化

看看初始化一個空列表的時候發(fā)生了什么,例如:l = []。

  1. arguments: size of the list = 0 
  2.  
  3. returns: list object = [] 
  4.  
  5. PyListNew: 
  6.  
  7.     nbytes = size * size of global Python object = 0 
  8.  
  9.     allocate new list object 
  10.  
  11.     allocate list of pointers (ob_item) of size nbytes = 0 
  12.  
  13.     clear ob_item 
  14.  
  15.     set list's allocated var to 0 = 0 slots 
  16.  
  17.     return list object  

要分清列表大小和分配的槽大小,這很重要。列表的大小和 len(l) 的大小相同。分配槽的大小是指已經(jīng)在內(nèi)存中分配了的槽空間數(shù)。通常分配的槽的大小要大于列表大小,這是為了避免每次列表添加元素的時候都調(diào)用分配內(nèi)存的函數(shù)。下面會具體介紹。

Append 操作

向列表添加一個整數(shù):l.append(1) 時發(fā)生了什么?調(diào)用了底層的 C 函數(shù) app1()。

  1. arguments: list object, new element 
  2.  
  3. returns: 0 if OK, -1 if not 
  4.  
  5. app1: 
  6.  
  7.     n = size of list 
  8.  
  9.     call list_resize() to resize the list to size n+1 = 0 + 1 = 1 
  10.  
  11.     list[n] = list[0] = new element 
  12.  
  13.     return 0  

下面是 list_resize() 函數(shù)。它會多申請一些內(nèi)存,避免頻繁調(diào)用 list_resize() 函數(shù)。列表的增長模式為:0,4,8,16,25,35,46,58,72,88……

  1. arguments: list object, new size 
  2.  
  3. returns: 0 if OK, -1 if not 
  4.  
  5. list_resize: 
  6.  
  7.     new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6) = 3 
  8.  
  9.     new_allocated += newsize = 3 + 1 = 4 
  10.  
  11.     resize ob_item (list of pointers) to size new_allocated 
  12.  
  13.     return 0  

現(xiàn)在分配了 4 個用來裝列表元素的槽空間,并且***個空間中為整數(shù) 1。如下圖顯示 l[0] 指向我們新添加的整數(shù)對象。虛線的方框表示已經(jīng)分配但沒有使用的槽空間。

列表追加元素操作的平均復雜度為 O(1)。

 

繼續(xù)添加新的元素:l.append(2)。調(diào)用 list_resize 函數(shù),參數(shù)為 n+1 = 2, 但是因為已經(jīng)申請了 4 個槽空間,所以不需要再申請內(nèi)存空間。再添加兩個整數(shù)的情況也是一樣的:l.append(3),l.append(4)。下圖顯示了我們現(xiàn)在的情況。  

 

Insert 操作

在列表偏移量 1 的位置插入新元素,整數(shù) 5:l.insert(1,5),內(nèi)部調(diào)用ins1() 函數(shù)。

  1. arguments: list object, where, new element 
  2.  
  3. returns: 0 if OK, -1 if not 
  4.  
  5. ins1: 
  6.  
  7.     resize list to size n+1 = 5 -> 4 more slots will be allocated 
  8.  
  9.     starting at the last element up to the offset whereright shift each element 
  10.  
  11.     set new element at offset where 
  12.  
  13.     return 0  

 

虛線的方框依舊表示已經(jīng)分配但沒有使用的槽空間?,F(xiàn)在分配了 8 個槽空間,但是列表的大小卻只是 5。

列表插入操作的平均復雜度為 O(n)。

Pop 操作

取出列表***一個元素 即l.pop(),調(diào)用了 listpop() 函數(shù)。在 listpop() 函數(shù)中會調(diào)用 list_resize 函數(shù),如果取出元素后列表的大小小于分配的槽空間數(shù)的一半,將會縮減列表的大小。

  1. arguments: list object 
  2.  
  3. returns: element popped 
  4.  
  5. listpop: 
  6.  
  7.     if list empty: 
  8.  
  9.         return null 
  10.  
  11.     resize list with size 5 - 1 = 4. 4 is not less than 8/2 so no shrinkage 
  12.  
  13.     set list object size to 4 
  14.  
  15.     return last element  

列表 pop 操作的平均復雜度為 O(1)。 

 

可以看到 pop 操作后槽空間 4 依然指向原先的整數(shù)對象,但是最為關鍵的是現(xiàn)在列表的大小已經(jīng)變?yōu)?4。

繼續(xù) pop 一個元素。在 list_resize() 函數(shù)中,size – 1 = 4 – 1 = 3 已經(jīng)小于所分配的槽空間大小的一半,所以縮減分配的槽空間為 6,同時現(xiàn)在列表的大小為 3。

可以看到槽空間 3 和 4 依然指向原先的整數(shù),但是現(xiàn)在列表的大小已經(jīng)變?yōu)?3。

 

Remove 操作

Python 的列表對象有個方法,刪除指定的元素: l.remove(5)。底層調(diào)用 listremove() 函數(shù)。

  1. arguments: list object, element to remove 
  2.  
  3. returns none if OK, null if not 
  4.  
  5. listremove: 
  6.  
  7.     loop through each list element: 
  8.  
  9.         if correct element: 
  10.  
  11.             slice list between element's slot and element's slot + 1 
  12.  
  13.             return none 
  14.  
  15.     return null  

為了做列表的切片并且刪除元素,調(diào)用了 list_ass_slice() 函數(shù),它的實現(xiàn)方法比較有趣。我們在刪除列表位置 1 的元素 5 的時候,低位的偏移量為 1 同時高位的偏移量為 2.

  1. arguments: list object, low offset, high offset 
  2.  
  3. returns: 0 if OK 
  4.  
  5. list_ass_slice: 
  6.  
  7.     copy integer 5 to recycle list to dereference it 
  8.  
  9.     shift elements from slot 2 to slot 1 
  10.  
  11.     resize list to 5 slots 
  12.  
  13.     return 0  

列表 remove 操作的復雜度為 O(n)。

 

責任編輯:龐桂玉 來源: Python開發(fā)者
相關推薦

2017-05-22 15:42:39

Python字典哈希表

2021-04-27 08:54:43

ConcurrentH數(shù)據(jù)結(jié)構(gòu)JDK8

2025-04-07 11:10:00

Python列表開發(fā)

2010-07-13 10:13:35

Perl內(nèi)部函數(shù)

2021-09-03 09:55:43

架構(gòu)Yarn內(nèi)部

2023-11-23 19:30:35

Python編程語言

2017-09-05 08:08:37

asyncio程序多線程

2021-08-19 16:56:37

Python內(nèi)存開發(fā)

2010-09-25 15:59:54

JVM虛擬機

2017-06-13 12:40:47

Python字符串對象

2021-08-12 15:45:23

Pythonimport模塊

2014-04-23 14:40:06

iOS開發(fā)KVO內(nèi)部實現(xiàn)

2009-11-03 13:33:39

VB.NET對象列表

2015-07-28 10:06:03

C#內(nèi)部實現(xiàn)剖析

2022-10-26 15:22:31

React組件User組件

2010-03-05 13:38:13

Python數(shù)據(jù)轉(zhuǎn)換

2016-10-20 08:46:17

2024-11-15 06:00:00

Python列表字典

2024-07-05 10:47:15

2024-07-11 11:35:08

數(shù)組結(jié)構(gòu)內(nèi)部機制
點贊
收藏

51CTO技術(shù)棧公眾號