當前位置:
首頁 > 知識 > Python 源碼閱讀:tuple

Python 源碼閱讀:tuple

(點擊

上方藍字

,快速關注我們)




來源:伯樂在線 - wklken


如有好文章投稿,請點擊 → 這裡了解詳情




示例





>>>

a

=

()


>>>

b

=

()

>>>

id

(

a

)

==

id

(

b

)


True


 


>>>

a

=

(

1

,

)


>>>

b

=

(

1

,

)


>>>

id

(

a

)

==

id

(

b

)


False




源碼位置 Include/tupleobject.h | Objects/tupleobject.c




結構




定義





typedef

struct

{


    

PyObject_VAR_HEAD


    PyObject *

ob_item

[

1

];


 


}

PyTupleObject

;




說明





1.

PyObject_VAR_HEAD


PyTupleObject

在底層是個變長對象

(

需要存儲列表元素個數

).


雖然

,

python

,

tuple

是不可變對象


 


2.

PyObject *

ob_item

[

1

];


指向存儲元素的數組




結構





構造方法





PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size);




構造




看下構造方法定義





PyObject *


PyTuple_New

(

register Py_ssize_t

size

)


{


    

register PyTupleObject *

op

;


    

Py_ssize

_

t

i

;


 


    

// 大小為負數, return


    

if

(

size

  

0

)

{


        

PyErr_BadInternalCall

();


        

return

NULL

;


    

}


 


    

// 如果大小=0, 空元組, 直接取free_list第一個返回


#if PyTuple_MAXSAVESIZE > 0


    

if

(

size

==

0

&

free_list

[

0

])

{


        

op

=

free_list

[

0

];


        

Py_INCREF

(

op

);


 


#ifdef COUNT_ALLOCS


        

tuple_zero_allocs

++

;


#endif


 


        

return

(

PyObject *

)

op

;


    

}


 


    

// 如果free_list可分配, 從free_list取一個


    

if

(

size  

PyTuple_MAXSAVESIZE

&

(

op

=

free_list

[

size

])

!=

NULL

)

{


        

// 上面  op = free_list[size] 取得單鏈表頭


        

// free_list指向單鏈表下一個元素, 對應位置閾值--


        

free_list

[

size

]

=

(

PyTupleObject *

)

op

->

ob_item

[

0

];


        

numfree

[

size

]

--

;


 


#ifdef COUNT_ALLOCS


        

fast_tuple_allocs

++

;


#endif


 


      

// 初始化 ob_size和ob_type


      

/* Inline PyObject_InitVar */


#ifdef Py_TRACE_REFS


        

Py_SIZE

(

op

)

=

size

;


        

Py_TYPE

(

op

)

= &

PyTuple_Type

;


#endif


        

_Py_NewReference

((

PyObject *

)

op

);


    

}


    

else


#endif   // free_list不可用


    

{


        

// 計算空間


        

Py_ssize_t

nbytes

=

size *

sizeof

(

PyObject *

);


        

/* Check for overflow */


        

if

(

nbytes

/

sizeof

(

PyObject *

)

!=

(

size_t

)

size

||


            

(

nbytes

>

PY_SSIZE_T_MAX

-

sizeof

(

PyTupleObject

)

-

sizeof

(

PyObject *

)))


        

{


            

return

PyErr_NoMemory

();


        

}


 


        

// 分配內存


        

op

=

PyObject_GC_NewVar

(

PyTupleObject

,

&

PyTuple_Type

,

size

);


        

if

(

op

==

NULL

)


            

return

NULL

;


    

}



 


    

// 初始化ob_item每個元素


    

for

(

i

=

0

;

i

  

size

;

i

++

)


        

op

->

ob_item

[

i

]

=

NULL

;


 


    

// 第一次分配空數組, 將其放入free_list第一個位置


#if PyTuple_MAXSAVESIZE > 0


    

if

(

size

==

0

)

{


        

free_list

[

0

]

=

op

;


        ++

numfree

[

0

];


        

Py_INCREF

(

op

);

          

/* extra INCREF so that this is never freed */


    

}


#endif


 


#ifdef SHOW_TRACK_COUNT


    

count_tracked

++

;


#endif


 


    

_PyObject_GC_TRACK

(

op

);


 


    

// 返回


    

return

(

PyObject *

)

op

;


}






簡化步驟






1.

如果

size

=

0

,

free_list

[

0

]

,

直接返回


 


2.

否則

,

確認

free_list

[

size

],

是否可用

,

可用獲取


 


3.

否則

,

從內存分配新的空間


 


4.

初始化

,

返回




回收




定義





static

void


tupledealloc

(

register PyTupleObject *

op

)


{


    

register

Py_ssize

_

t

i

;


    

// 獲取元素個數


    

register Py_ssize_t

len

=  

Py_SIZE

(

op

);


 


    

PyObject_GC_UnTrack

(

op

);


    

Py_TRASHCAN_SAFE_BEGIN

(

op

)


 


    

if

(

len

>

0

)

{


        

i

=

len

;


        

// 遍歷, 析構每個元素


        

while

(

--

i

>=

0

)


            

Py_XDECREF

(

op

->

ob_item

[

i

]);


 


        

// 與對象緩衝池相關


#if PyTuple_MAXSAVESIZE > 0


        

if

(

len

ob_item

[

0

]

=

(

PyObject *

)

free_list

[

len

];


            

numfree

[

len

]

++

;


            

free_list

[

len

]

=

op

;


            

goto

done

;

/* return */


        

}


#endif


 


    

}


    

// 調用回收


    

Py_TYPE

(

op

)

->

tp_free

((

PyObject *

)

op

);


 


done

:


    

Py_TRASHCAN_SAFE_END

(

op

)


}




簡化流程





1.

回收

ob

_

item每個元素


 


2.

如果符合條件

,

放入到

free

_

list


 


3.

否則

,

回收




tuple對象緩衝池




定義





/* Speed optimization to avoid frequent malloc/free of small tuples */


#ifndef PyTuple_MAXSAVESIZE


#define PyTuple_MAXSAVESIZE     20


#endif


 


#ifndef PyTuple_MAXFREELIST


#define PyTuple_MAXFREELIST  2000


#endif


 


#if PyTuple_MAXSAVESIZE > 0


 


static

PyTupleObject *

free_list

[

PyTuple_MAXSAVESIZE

];


static

int

numfree

[

PyTuple_MAXSAVESIZE

];


#endif




結論





1.

作用

:

優化小

tuple

mall

/

free


 


2.

PyTuple_MAXSAVESIZE

=

20


會被緩存的

tuple

長度閾值

,

20

,

長度




free_list的結構





回頭看回收跟對象緩衝池相關的邏輯




條件:





if

(

len

<

PyTuple_MAXSAVESIZE

&&         //

len

<

20


    

numfree

[

len

]

<

PyTuple_MAXFREELIST

&& //

numfree

[

len

]

<

2000


    

Py_TYPE

(

op

)

== &

PyTuple_Type

)

//

tuple

類型




操作





op

->

ob_item

[

0

]

=

(

PyObject *

)

free_list

[

len

];

//ob_item指向free_list[len] 單鏈表頭


numfree

[

len

]

++

;

  

// len位置計數+1


free_list

[

len

]

=

op

;

// op變成單鏈表的頭


goto

done

;

/* return */




即過程





1.

如果

size

=

0

,

直接從

free_list

[

0

]


 


2.

如果

size

!=

0

,

判斷

size

20

  

走內存分配邏輯


 


------------------


 


回收時


 


如果

size




注意





1.

回收時

,

ob

_

item都會被回收

,

只是本身對象緩存下來


 


2.

這裡

free_list

,

復用

ob

_

item作為鏈表指針

,

指向下一個位置

(

通用整數對象池也是復用指針的方式

,

不過用的是

ob_type

)




本系列




  • 《Python 源碼閱讀:類型》



  • 《Python 源碼閱讀:對象》



  • 《Python 源碼閱讀:String》



  • 《Python 源碼閱讀:int》



  • 《Python 源碼閱讀:list》




看完本文有收穫?請轉

發分享給更多人


關注「P

ython開發者」,提升Python技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發者 的精彩文章:

Flask 中模塊化應用的實現
編程零基礎,如何變身年薪百萬的機器學習工程師?
讓你的 Python 代碼優雅又地道,15篇 Python 技術熱文
Python 源碼閱讀:list
Flask 應用中的 URL 處理

TAG:Python開發者 |