標籤:python 源碼 基礎
python源碼剖析筆記1——Python對象初見
工作整兩年了,用python最多,然而對於python內部機制不一定都清楚,每天沉醉於增刪改查的簡單邏輯編寫,實在耗神。很多東西不用就忘記了,比如C語言,正好,python源碼用C寫的,分析python源碼的同時又能溫故C語言基礎,實在是件很好的事情。另外,還有陳儒大神的《python源碼剖析》做指引,分析也不至於沒頭沒腦。期望在一個月的業餘時間,能有所小成,以此為記。
1 python中的對象
python中,一切東西都是對象,在c語言實現中對應著結構體。首先當然還是從python內建對象開始看起,最基本的是PyIntObject, PyStringObject, PyListObject, PyDictObject這幾個,他們分別屬於int,string, list, dict類型。從python2.2之後有了new style class之後,這些內建對象都是繼承自object類型,object在代碼中對應PyBaseObject_Type。比如我們指派陳述式a=3,那麼a就是一個PyIntObject對象,它的類型是int,在代碼中對應PyInt_Type,PyInt_Type也是一種對象,我們稱之為類型對象。那麼PyInt_Type它的類型是什麼呢,答案是type, 對應到代碼中就是PyType_Type。當然object也是一個類型對象,它的類型也是PyType_Type。這麼一層層下去,PyType_Type也是個對象,那它的類型又是什麼呢,沒錯,答案就是它的類型就是它自己,。看下面的驗證代碼:
##內建對象測試In [1]: a = 3In [2]: type(a)Out[2]: intIn [3]: type(int)Out[3]: typeIn [4]: type(type)Out[4]: typeIn [5]: int.__base__Out[5]: objectIn [6]: type(object)Out[6]: type
先分析下幾個基礎內建對象在C語言中的結構體以及常用的幾個宏,為了方便,我用的也是陳儒大神分析的那個版本一致,版本是2.5.6.源碼官網有下載。
// 內建對象基礎#define PyObject_HEAD \ Py_ssize_t ob_refcnt; struct _typeobject *ob_type;#define PyObject_HEAD_INIT(type) \ 1, type,#define PyObject_VAR_HEAD \ PyObject_HEAD Py_ssize_t ob_size; /* Number of items in variable part */#define Py_INVALID_SIZE (Py_ssize_t)-1typedef struct _object { PyObject_HEAD} PyObject;typedef struct { PyObject_VAR_HEAD} PyVarObject;typedef struct _typeobject { PyObject_VAR_HEAD const char *tp_name; /* For printing, in format "<module>.<name>" */ Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */ destructor tp_dealloc; printfunc tp_print; getattrfunc tp_getattr; setattrfunc tp_setattr; cmpfunc tp_compare; reprfunc tp_repr; ...} PyTypeObject;typedef struct { PyObject_HEAD long ob_ival;} PyIntObject;typedef struct { PyObject_VAR_HEAD long ob_shash; int ob_sstate; char ob_sval[1]; /* Invariants: * ob_sval contains space for ‘ob_size+1‘ elements. * ob_sval[ob_size] == 0. * ob_shash is the hash of the string or -1 if not computed yet. * ob_sstate != 0 iff the string object is in stringobject.c‘s * ‘interned‘ dictionary; in this case the two references * from ‘interned‘ to this object are *not counted* in ob_refcnt. */} PyStringObject;
如代碼中所示,PyObject是所有Python對象的基石,所有後續看到的對象都有一個相同的PyObject頭部,從而我們可以在源碼中看到所有的對象都可以用PyObject*指標指向,這就是物件導向中經常用到的多態的技巧了。Python內部各個函數對象間也是通過PyObject*傳遞,即便本身這是一個PyIntObject類型的對象,代碼中並不會用PyIntObject*指標進行傳遞,這也是為了實現多態。比如下面的函數:
void Print(PyObject* object) { object->ob_type->tp_print(object);}
另外如代碼中注釋所說的,變長對象的ob_size指的是元素個數,不是位元組數目。
2 python對象引用計數
下面是幾個常用的操作對象引用計數的宏定義(object.h),一併列出,這裡去除了一些調試時用的代碼,更容易看明白代碼含義。Py_NewReference是初始化時對象時設定引用計數, Py_INCREF和Py_DECREF分別用來增加引用技術和減少引用計數。從代碼中可以看到,python增加引用和減少引用都是通過這些宏操作的,**有一點需要注意的是,當對象引用ob_refcnt減小到0時,會調用對象的解構函式,解構函式並不一定會調用free釋放記憶體空間,因為頻繁申請和釋放記憶體嚴重影響效能,所以在後面看到python有大量用到記憶體池技術,對提升效能有很大效果。
需要說明的是,類型對象是不在引用計數規則之中的,每個對象指向類型對象的指標並不視為類型對象的引用,也就是說不會影響類型對象的引用計數,類型對象永遠不會被析構。
#define _Py_NewReference(op) ((op)->ob_refcnt = 1)#define _Py_Dealloc(op) (*(op)->ob_type->tp_dealloc)((PyObject *)(op)))#define Py_INCREF(op) ((op)->ob_refcnt++)#define Py_DECREF(op) \ if (--(op)->ob_refcnt != 0) ; else _Py_Dealloc((PyObject *)(op))#define Py_CLEAR(op) do { if (op) { PyObject *tmp = (PyObject *)(op); (op) = NULL; Py_DECREF(tmp); } } while (0)/* Macros to use in case the object pointer may be NULL: */#define Py_XINCREF(op) if ((op) == NULL) ; else Py_INCREF(op)#define Py_XDECREF(op) if ((op) == NULL) ; else Py_DECREF(op)
3 Python對象分類
python中的對象大致可以分為下面幾類:
- 數值對象:如integer,float,boolean
- 序列集合對象:如string,list,tuple
- 字典對象:如dict
- 類型對象:如type
- 內部對象:如後面會看到的code,function,frame,module以及method對象等。
4 參考資料
python源碼剖析筆記1——Python對象初見