1. 程式人生 > >Python 原始碼剖析(一)【python物件】

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

處於研究python記憶體釋放問題,在閱讀部分python原始碼,順便記錄下所得。
(基於《python原始碼剖析》(v2.4.1)與 python原始碼(v2.7.6))

先列下總結:
        python 中一切皆為物件,所以會先講明白python中的物件,然後開始整理最簡單的兩個型別,整形和字串;然後會進一步探索容器型別,會講 List 和 Dict,以及記憶體管理機制。有時間精力會總結下《python原始碼剖析》作者的 python模擬程式、編譯的code物件與pyc檔案、python虛擬機器相關知識,執行環境,模組動態載入、多執行緒機制。

 

直入主題,開始總結。


一、Python物件

1、物件

2、物件型別

3、繼承與多型

4、引用計數

5、物件分類


1、物件

python中一切皆為物件,包括自定義型別,int、str、list、dict等都是物件,先看看所有物件的基石:

*定長物件(int,str):

[object.h]

typedef struct_object{

PyObject_HEAD

}PyObject;

*變長物件(list,dict..):

[object.h]

typedef 

struct{

PyObject_VAR_HEAD

}PyVarObject;

//前者依賴於PyObject_HEAD,後者依賴於PyObject_VAR_HEAD,看看兩者不同:

圖1-1-1

由圖1-1-1可見,定長物件中有

ob_refcntob_type這兩個變數,變長物件中多了一個ob_size變數;其中,ob_refcnt用於引用計數機制,ob_type是一個指向_typeobject結構體的指標,ob_size指變長物件中包含的元素個數。

ob_refcnt後面記憶體回收時再講,大概就是某個物件A,對其有引用時引用計數增加,釋放時引用計數減少,引用計數為0時將回收物件A,從堆上刪除釋放記憶體。

ob_type_typeobject結構體,用於指定一個物件型別的型別物件。有點拗口,下節講。

ob_size用於指明容器物件中擁有元素的個數,不是位元組數。


2、物件型別

現在分析上節提到的_typeobject,型別物件

 

 

 

圖1-2-1

_typeobject是比較大的一個結構體,主要有四類資訊:

1、型別名tp_name,用於pyhton內部及除錯;

2、建立該型別物件時分配記憶體空間大小資訊,tp_basicsizetp_itemsize

3、與該物件有關的操作資訊,如hashfunc(函式指標),操作主要分為標準操作、標準操作族、其他操作;

4、型別資訊;

_typeobject頭部中有PyObject_VAR_HEAD,說明型別也是一個物件,而型別物件的型別則是PyType_Type(圖1-2-2):

圖1-2-2

例如整形int(圖1-2-3):

圖1-2-3

其執行時物件型別關係(圖1-2-4):

圖1-2-4


3、繼承與多型

通過前面的PyObject和型別物件,Python利用C語言實現繼承和多型。首先,Python中所有內建物件和內部使用物件在最開始記憶體區域都有一個PyObject,相當於這些物件都繼承於PyObject;在建立一個物件時,如PyIntObject,這物件由PyObject*維護而非PyIntObject*維護,而這指標指向的型別只能從ob_type域判斷,從而實現多型。


4、引用計數

Python內建了垃圾收集機制,使用每個物件共有的ob_refcnt來維護引用計數,通過PyINCREF(op)和Py_DECREF(op)兩個巨集來增減物件引用計數,當引用計數為0後通過tp_dealloc釋放其記憶體和系統資源。在物件初始化時,通過_Py_NewReference(op)將物件引用計數初始化為1。(程式碼如圖1-4-1)

圖1-4-1

還有要注意的是,但引用計數減為0時,會呼叫該物件的解構函式,但不一定會呼叫free,頻繁申請、釋放記憶體降會低執行效率,Python使用記憶體池計數作為補充。


5、物件分類

1、Math:數值物件

2、Container:容器物件

3、Composition:程式結構物件

4、Internal:內部使用物件

圖示(圖1-5-1):

圖1-5-1