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_refcnt、ob_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_basicsize和tp_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